Vue的详细解析是怎样的?

2026-04-27 18:301阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1963个文字,预计阅读时间需要8分钟。

Vue的详细解析是怎样的?

目录 + initState + 状态初始化 + 1. initProps + 2. initSetup + 3. initMethods + 4. initData + 5. initComputed + 与 + initWatch + initState + 状态初始化 + 在配置标准化合并以及声明周期初始化完成后,将调用 + callHook(beforeCreate)

目录
  • initState 状态初始化
    • 1. initProps
    • 2. initSetup
    • 3. initMethods
    • 4. initData
    • 5. initComputed 与 initWatch

initState 状态初始化

在配置标准化合并以及声明周期初始化完成之后,会调用callHook('beforeCreate')来表示组件已进入正式实例化阶段。

Vue的详细解析是怎样的?

这个时候会对数据、方法、监听器等配置项进行对应的处理,并且在开发环境还会进行一系列校验,抛出校验异常信息。整个数据的初始化过程是initInjection => initState => initProvide,但是injection/provide一般是一起使用,所以这里也替换一下顺序,将这两者放到后面一起分析。

首先是initState的函数定义:

 export function initState(vm: Component) {    const opts = vm.$options    if (opts.props) initProps(vm, opts.props)    // Composition API    initSetup(vm)    if (opts.methods) initMethods(vm, opts.methods)    if (opts.data) {      initData(vm)   } else {      const ob = observe((vm._data = {}))      ob && ob.vmCount++   }    if (opts.computed) initComputed(vm, opts.computed)    if (opts.watch && opts.watch !== nativeWatch) {      initWatch(vm, opts.watch)   }  }

整个过程其实十分清晰:

  • initProps:初始化props组件参数配置
  • initSetup:解析setup配置,处理setup的返回值(这里主要是 2.7 版本之后为了适配 v3 语法新增的内容)
  • initMethods:初始化组件方法
  • initData:初始化组件内变量
  • initComputed:初始化组件计算属性
  • initWatch:初始化组件内部监听器

1. initProps

该函数定义如下:

 function initProps(vm: Component, propsOptions: Object) {    const propsData = vm.$options.propsData || {}    const props = (vm._props = shallowReactive({}))    const keys: string[] = (vm.$options._propKeys = [])    const isRoot = !vm.$parent        if (!isRoot) {      toggleObserving(false)   }    for (const key in propsOptions) {      keys.push(key)      const value = validateProp(key, propsOptions, propsData, vm)      if (__DEV__) {        const hyphenatedKey = hyphenate(key)        if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) {          warn('')       }        defineReactive(props, key, value, () => {          if (!isRoot && !isUpdatingChildComponent) {            warn('')         }       })     } else {        defineReactive(props, key, value)     }      if (!(key in vm)) {        proxy(vm, `_props`, key)     }   }    toggleObserving(true)  }

这个过程会遍历整个组件的props配置项(mergeOptions 之后,已包含继承和混入),并将每个prop key从驼峰形式转换为-短横线连接的形式。

这也是为什么官方推荐props配置使用小写驼峰,而组件使用时绑定参数使用- 短横线的原因。

之后,则是校验每个propkey是否符合规范(即不是 Vue 的内置关键字 ref,key 等,也是不是配置里面isReservedAttr(key)禁止的属性名);最后,通过defineReactive来对prop进行响应式处理,并挂载到vm._props中。

当然,上面的响应式处理只针对根组件,如果不是根组件的话,是会在函数前面部分调用toggleObserving(false)来关闭响应式处理

2. initSetup

这个部分是为了适配 v3 语法新增的一部分,这里就不放源码,只简单介绍一下。

该方法位于src/v3/apiSetup.ts文件内,在执行过程中,主要有以下几步:

  • 通过createSetupContext(vm)创建一个setup函数执行过程中的上下文对象,该对象包括attrs,listeners,slots,emit几个属性,以及一个expose方法;然后将这个上下文对象绑定到组件的_setupContext属性上,最后调用setCurrentInstance将当前上下文实例指定为当前组件实例vm
  • 调用pushTarget()阻止过程中的依赖收集,并调用invokeWithErrorHandling来获取setup函数的返回值
  • 然后删除setup中的当前实例上下文,调用popTarget恢复依赖收集
  • 判断setup函数的执行返回值,如果是函数,则说明返回的是render,将该返回值赋值给options用于后面执行mount渲染;如果是对象,则会将返回值赋值给vm._setupState,然后遍历返回值对象,进行响应式处理

3. initMethods

函数定义如下:

 function initMethods(vm: Component, methods: Object) {    const props = vm.$options.props    for (const key in methods) {      if (__DEV__) {        if (typeof methods[key] !== 'function') {          warn('')       }        if (props && hasOwn(props, key)) {          warn('')       }        if (key in vm && isReserved(key)) {          warn('')       }     }      vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)   }  }

这部分就十分十分简单了,就是通过Function.bind函数来更改组件配置options中的每一个方法的 this 指向,最后重新绑定到当前组件上。

这里的校验其实就是校验名字是否会和 js 的内置方法名冲突,或者与 props 中存在同名方法。

4. initData

在执行initData之前,会校验options中有没有 data 配置,没有则会初始化为一个空对象。

其函数定义如下:

 function initData(vm: Component) {    let data: any = vm.$options.data    data = vm._data = isFunction(data) ? getData(data, vm) : data || {}    if (!isPlainObject(data)) {      data = {}      __DEV__ && warn('')   }    const keys = Object.keys(data)    const props = vm.$options.props    const methods = vm.$options.methods    let i = keys.length    while (i--) {      const key = keys[i]      if (__DEV__) {        if (methods && hasOwn(methods, key)) {          warn('')       }     }      if (props && hasOwn(props, key)) {        __DEV__ && warn()     } else if (!isReserved(key)) {        proxy(vm, `_data`, key)     }   }    const ob = observe(data)    ob && ob.vmCount++  }

这里其实也比较简单,就是校验是否有与 props 或者 methods 同名的数据,并将其代理到vm._data上,最后通过observe方法对数据进行响应式处理。

5. initComputed 与 initWatch

这两部分主要是配置对dataprops的变量的变化侦测(监听),因为涉及到 Vue 的响应式系统中的Watcher观察者定义 与 依赖收集系统,整体的内容比较多,所以后面整体讲。

简单分析两者的基本逻辑:

initComputed:

  • 获取options.computed里定义的每个计算属性的get方法作为getter(如果就是一个函数,则这个函数直接作为 getter)
  • 如果该计算属性的 key不能在当前的实例上找到,则直接通过defineComputed定义一个计算属性
  • 如果能找到,则判断是否是在data,methods,props中,并报出对应错误

initWatch:

这个过程则更加简单,因为不用校验 key 的重复性,所以会直接遍历options.watch,如果某个属性的监听器有两个 handler 方法,还会将方法提出来,最后调用createWatcher来创建监听器。

到此这篇关于详解Vue 2中的 initState 状态初始化的文章就介绍到这了,更多相关Vue initState 内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

标签:initState

本文共计1963个文字,预计阅读时间需要8分钟。

Vue的详细解析是怎样的?

目录 + initState + 状态初始化 + 1. initProps + 2. initSetup + 3. initMethods + 4. initData + 5. initComputed + 与 + initWatch + initState + 状态初始化 + 在配置标准化合并以及声明周期初始化完成后,将调用 + callHook(beforeCreate)

目录
  • initState 状态初始化
    • 1. initProps
    • 2. initSetup
    • 3. initMethods
    • 4. initData
    • 5. initComputed 与 initWatch

initState 状态初始化

在配置标准化合并以及声明周期初始化完成之后,会调用callHook('beforeCreate')来表示组件已进入正式实例化阶段。

Vue的详细解析是怎样的?

这个时候会对数据、方法、监听器等配置项进行对应的处理,并且在开发环境还会进行一系列校验,抛出校验异常信息。整个数据的初始化过程是initInjection => initState => initProvide,但是injection/provide一般是一起使用,所以这里也替换一下顺序,将这两者放到后面一起分析。

首先是initState的函数定义:

 export function initState(vm: Component) {    const opts = vm.$options    if (opts.props) initProps(vm, opts.props)    // Composition API    initSetup(vm)    if (opts.methods) initMethods(vm, opts.methods)    if (opts.data) {      initData(vm)   } else {      const ob = observe((vm._data = {}))      ob && ob.vmCount++   }    if (opts.computed) initComputed(vm, opts.computed)    if (opts.watch && opts.watch !== nativeWatch) {      initWatch(vm, opts.watch)   }  }

整个过程其实十分清晰:

  • initProps:初始化props组件参数配置
  • initSetup:解析setup配置,处理setup的返回值(这里主要是 2.7 版本之后为了适配 v3 语法新增的内容)
  • initMethods:初始化组件方法
  • initData:初始化组件内变量
  • initComputed:初始化组件计算属性
  • initWatch:初始化组件内部监听器

1. initProps

该函数定义如下:

 function initProps(vm: Component, propsOptions: Object) {    const propsData = vm.$options.propsData || {}    const props = (vm._props = shallowReactive({}))    const keys: string[] = (vm.$options._propKeys = [])    const isRoot = !vm.$parent        if (!isRoot) {      toggleObserving(false)   }    for (const key in propsOptions) {      keys.push(key)      const value = validateProp(key, propsOptions, propsData, vm)      if (__DEV__) {        const hyphenatedKey = hyphenate(key)        if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) {          warn('')       }        defineReactive(props, key, value, () => {          if (!isRoot && !isUpdatingChildComponent) {            warn('')         }       })     } else {        defineReactive(props, key, value)     }      if (!(key in vm)) {        proxy(vm, `_props`, key)     }   }    toggleObserving(true)  }

这个过程会遍历整个组件的props配置项(mergeOptions 之后,已包含继承和混入),并将每个prop key从驼峰形式转换为-短横线连接的形式。

这也是为什么官方推荐props配置使用小写驼峰,而组件使用时绑定参数使用- 短横线的原因。

之后,则是校验每个propkey是否符合规范(即不是 Vue 的内置关键字 ref,key 等,也是不是配置里面isReservedAttr(key)禁止的属性名);最后,通过defineReactive来对prop进行响应式处理,并挂载到vm._props中。

当然,上面的响应式处理只针对根组件,如果不是根组件的话,是会在函数前面部分调用toggleObserving(false)来关闭响应式处理

2. initSetup

这个部分是为了适配 v3 语法新增的一部分,这里就不放源码,只简单介绍一下。

该方法位于src/v3/apiSetup.ts文件内,在执行过程中,主要有以下几步:

  • 通过createSetupContext(vm)创建一个setup函数执行过程中的上下文对象,该对象包括attrs,listeners,slots,emit几个属性,以及一个expose方法;然后将这个上下文对象绑定到组件的_setupContext属性上,最后调用setCurrentInstance将当前上下文实例指定为当前组件实例vm
  • 调用pushTarget()阻止过程中的依赖收集,并调用invokeWithErrorHandling来获取setup函数的返回值
  • 然后删除setup中的当前实例上下文,调用popTarget恢复依赖收集
  • 判断setup函数的执行返回值,如果是函数,则说明返回的是render,将该返回值赋值给options用于后面执行mount渲染;如果是对象,则会将返回值赋值给vm._setupState,然后遍历返回值对象,进行响应式处理

3. initMethods

函数定义如下:

 function initMethods(vm: Component, methods: Object) {    const props = vm.$options.props    for (const key in methods) {      if (__DEV__) {        if (typeof methods[key] !== 'function') {          warn('')       }        if (props && hasOwn(props, key)) {          warn('')       }        if (key in vm && isReserved(key)) {          warn('')       }     }      vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)   }  }

这部分就十分十分简单了,就是通过Function.bind函数来更改组件配置options中的每一个方法的 this 指向,最后重新绑定到当前组件上。

这里的校验其实就是校验名字是否会和 js 的内置方法名冲突,或者与 props 中存在同名方法。

4. initData

在执行initData之前,会校验options中有没有 data 配置,没有则会初始化为一个空对象。

其函数定义如下:

 function initData(vm: Component) {    let data: any = vm.$options.data    data = vm._data = isFunction(data) ? getData(data, vm) : data || {}    if (!isPlainObject(data)) {      data = {}      __DEV__ && warn('')   }    const keys = Object.keys(data)    const props = vm.$options.props    const methods = vm.$options.methods    let i = keys.length    while (i--) {      const key = keys[i]      if (__DEV__) {        if (methods && hasOwn(methods, key)) {          warn('')       }     }      if (props && hasOwn(props, key)) {        __DEV__ && warn()     } else if (!isReserved(key)) {        proxy(vm, `_data`, key)     }   }    const ob = observe(data)    ob && ob.vmCount++  }

这里其实也比较简单,就是校验是否有与 props 或者 methods 同名的数据,并将其代理到vm._data上,最后通过observe方法对数据进行响应式处理。

5. initComputed 与 initWatch

这两部分主要是配置对dataprops的变量的变化侦测(监听),因为涉及到 Vue 的响应式系统中的Watcher观察者定义 与 依赖收集系统,整体的内容比较多,所以后面整体讲。

简单分析两者的基本逻辑:

initComputed:

  • 获取options.computed里定义的每个计算属性的get方法作为getter(如果就是一个函数,则这个函数直接作为 getter)
  • 如果该计算属性的 key不能在当前的实例上找到,则直接通过defineComputed定义一个计算属性
  • 如果能找到,则判断是否是在data,methods,props中,并报出对应错误

initWatch:

这个过程则更加简单,因为不用校验 key 的重复性,所以会直接遍历options.watch,如果某个属性的监听器有两个 handler 方法,还会将方法提出来,最后调用createWatcher来创建监听器。

到此这篇关于详解Vue 2中的 initState 状态初始化的文章就介绍到这了,更多相关Vue initState 内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

标签:initState