Vue的初始化过程

370 阅读2分钟

Vue的初始化过程

一、Vue的构造函数

打开src/core/instance/index.js文件

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

// Vue的构造函数
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  // 根据options初始化
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

Vue的构造函数很简单,仅仅是使用_init函数进行了初始化,然后下面几行MixinVue的构造函数进行了扩展。

  1. initMixin:混入了init函数

    initMixin 函数位于src/core/instance/init.js文件,其作用就是在Vue的原型上添加_init函数。

    1. _init函数先使用mergeOptionsresolveConstructorOptions函数合并了构造函数默认的options

      
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
          options || {},
            vm
        )
      
      
    2. 然后做了如下操作


      /**
       * 1. 挂载Vue实例的父节点$parent
       * 2. 将本实例push到父节点的$children中
       * 3. 挂载Vue实例的$root节点
       * 4. 初始化children, refs, _watcher, _inactive, _directInactive,
       *     _isMounted, _isDestroyed, _isBeingDestroyed的值
       */
      initLifecycle(vm)
      /**
       * 获取父组件中附加到此实例上的事件,然后更新组件的事件监听
       */
      initEvents(vm)
      /**
       * 初始化渲染函数
       * 1. 挂载插槽内容,slot和scopedslot到实例上
       * 2. 绑定createElement函数到实例上(_c函数和$createElement函数,具体分析后面再说)
       * 3. 将$attrs和$listeners属性变温响应式属性
       */
      initRender(vm)
      /**
       * 调用beforeCreate钩子函数
       */
      callHook(vm, 'beforeCreate')
      // 初始化inject属性
      initInjections(vm) // resolve injections before data/props
      /**
       * 按照顺序依次
       * 1. 初始化props
       * 2. 初始化methods
       * 3. 初始化data
       * 4. 初始化computed
       * 5. 初始化watch
       */
      initState(vm)
      // 初始化provide
      initProvide(vm) // resolve provide after data/props
      /**
       * 调用created钩子函数
       */
      callHook(vm, 'created')
    
    1. 若el选项存在,通过el挂载Vue实例
      if (vm.$options.el) {
        vm.$mount(vm.$options.el)
      }
    
  2. stateMixin:扩展了实例状态相关的属性

    1. 代理了$data$props属性
        Object.defineProperty(Vue.prototype, '$data', dataDef)
        Object.defineProperty(Vue.prototype, '$props', propsDef)
      
    2. 挂载$set$delete$watch函数
      Vue.prototype.$set = set
      Vue.prototype.$delete = del
      
      Vue.prototype.$watch = function (
        expOrFn: string | Function,
        cb: any,
        options?: Object
      ): Function {
        const vm: Component = this
        if (isPlainObject(cb)) {
          return createWatcher(vm, expOrFn, cb, options)
        }
        options = options || {}
        options.user = true
        const watcher = new Watcher(vm, expOrFn, cb, options)
        if (options.immediate) {
          const info = `callback for immediate watcher "${watcher.expression}"`
          pushTarget()
          invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
          popTarget()
        }
        return function unwatchFn () {
          watcher.teardown()
        }
      }
      
下面的mixin夜做着类似的工作,就不多加赘述了, 具体的分析以后再来搞
  1. eventsMixin: 扩展事件相关函数
    1. $on
    2. $off
    3. $emit
    4. $once
  2. lifecycleMixin: 扩展生命周期相关函数
    1. Vue.prototype._update 函数
    2. $forceUpdate
    3. $destroy
  3. renderMixin: 扩展渲染相关函数
    1. 扩展运行时的工具函数
    2. 扩展$nextTick函数
    3. 扩展_render函数

二、说一下_init中的$mount过程

$mount函数实际上就是src/core/instance/lifecycle.js中的mountComponent函数

  1. 获取要更新的渲染函数_update
  2. 新建一个Watcher对象,before中调用beforeUpdate钩子
  3. 渲染更新完成后调用mounted钩子 此处涉及到了具体的patch、渲染过程以及响应式原理,先挖个坑,后面再填