vue源码分析-初始化操作

vue

function Vue (options) {
  // vue必须通过new关键字创建,否则报错
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

1. vue init方法

  1. 在原型上添加init方法,用于初始化
  2. 添加_uid,给vue实例加上唯一ID
  3. 判断是否是组件,如果是组件,需要初始化组件选项
      export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
        // 深拷贝options
        const opts = vm.$options = Object.create(vm.constructor.options)
        // doing this because it's faster than dynamic enumeration.
        // 赋值父节点
        const parentVnode = options._parentVnode
        opts.parent = options.parent
        opts._parentVnode = parentVnode
    
        const vnodeComponentOptions = parentVnode.componentOptions
        // 将父节点上的属性参数赋值
        opts.propsData = vnodeComponentOptions.propsData
        opts._parentListeners = vnodeComponentOptions.listeners
        opts._renderChildren = vnodeComponentOptions.children
        opts._componentTag = vnodeComponentOptions.tag
    
        // 查看有没有渲染函数,有则赋值
        if (options.render) {
          opts.render = options.render
          opts.staticRenderFns = options.staticRenderFns
        }
      }
    
  4. 如果不是组件,则直接执行数据合并 mergeOptions
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    
      export function resolveConstructorOptions (Ctor: Class) {
        // 获取当前构造函数上的options
        let options = Ctor.options
        // 查看当前构造函数是否是继承来的
        if (Ctor.super) {
          // 如果是继承来的,就合并父类的options到子类,递归查找,直到最顶层
          const superOptions = resolveConstructorOptions(Ctor.super)
          // 缓存本次的options
          const cachedSuperOptions = Ctor.superOptions
          // 如果两次的options不一样
          if (superOptions !== cachedSuperOptions) {
            // super option changed,
            // need to resolve new options.
            // 重置superOptions的值为本次循环options的值
            Ctor.superOptions = superOptions
            // check if there are any late-modified/attached options (#4976)
            // 检查两次options之间的修改值
            const modifiedOptions = resolveModifiedOptions(Ctor)
            // update base extend options
            // 将modifiedOptions上的值合并到extendOptions上面去
            if (modifiedOptions) {
              extend(Ctor.extendOptions, modifiedOptions)
            }
            // 执行mergeOptions方法,合并options,执行相关合并策略
            options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
            if (options.name) {
              options.components[options.name] = Ctor
            }
          }
        }
        return options
      }
    
主要是mergeOptions方法
  1. 检查component的名称是否是违禁的,不能使用HTML名称和关键字

  2. 由于vue的组件必须是function,所以需要判断下子类的类型,获取子类的options

    // 判断是否是组件
    if (typeof child === 'function') {
      child = child.options
    }
    
  3. 检查对应的选项的规范,排除不规范的选项

      normalizeProps(child, vm)
      normalizeInject(child, vm)
      normalizeDirectives(child)
    
  4. 将extends继承来的属性和mixin混合的属性都添加到options中去

    1. 遍历父级的options
    2. 找到对应的默认执行策略,如果没有,就用默认执行策略
    3. 遍历子类的options,添加子类的私有属性,默认的策略是如果子类有就用子类的,否则就用父类的
      // 遍历parent的值,看看child中是否有,如果有就替换
      for (key in parent) {
          mergeField(key)
      }
    
      // 遍历child,添加child私有属性值
      for (key in child) {
          if (!hasOwn(parent, key)) {
              mergeField(key)
          }
      }
      
      // 合并策略
      function mergeField (key) {
          // strats 是自定义策略的集合
          //  defaultStrat 是默认策略 
          const strat = strats[key] || defaultStrat
          options[key] = strat(parent[key], child[key], vm, key)
      }
    
      /**
      * Default strategy.
      * 判断子类是否存在,不存在就用父类的,否则就用子类的
      */
        const defaultStrat = function (parentVal: any, childVal: any): any {
            return childVal === undefined
                ? parentVal
                : childVal
        }
    
    1. data合并策略
      组件data必须为function,原因是为了提高复用性

        if (!vm) {
            // 判断子类如果data不是function,就报出警告
            if (childVal && typeof childVal !== 'function') {
                process.env.NODE_ENV !== 'production' && warn(
                'The "data" option should be a function ' +
                'that returns a per-instance value in component ' +
                'definitions.',
                vm
          )
      
            return parentVal
        }
        return mergeDataOrFn(parentVal, childVal)
      }
      

      具体合并逻辑

          /**
            * Helper that recursively merges two data objects together.
          */
        function mergeData (to: Object, from: ?Object): Object {
              if (!from) return to
              let key, toVal, fromVal
      
              const keys = hasSymbol
                        ? Reflect.ownKeys(from)
                      : Object.keys(from)
              
              // 遍历父类的data的数据
              for (let i = 0; i < keys.length; i++) {
                  key = keys[i]
                  // in case the object is already observed...
                  // 如果已经添加到响应式系统,就直接跳过
                  if (key === '__ob__') continue
                  // 子类的值
                  toVal = to[key]
                  // 父类的值
                  fromVal = from[key]
                  // 如果子类中不存在这个属性,就在子类中添加这个属性并赋值
                  if (!hasOwn(to, key)) {
                      set(to, key, fromVal)
                  } else if (
                      // 判断如果子类和父类都有对应的属性值,并且都是对象,就递归调用当前方法合并
                      toVal !== fromVal &&
                      isPlainObject(toVal) &&
                      isPlainObject(fromVal)
                  ) {
                      mergeData(toVal, fromVal)
                  }
            }
            return to
          }
      
    2. 合并component directive filter

          export const ASSET_TYPES = [
              'component',
              'directive',
              'filter'
          ]
      
          // 合并逻辑
          function mergeAssets (
              parentVal: ?Object,
              childVal: ?Object,
              vm?: Component,
              key: string
          ): Object {
              // 创建一个空对象
              const res = Object.create(parentVal || null)
              if (childVal) {
                    process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm)
                    // 将子类的属性赋值到空对象中
                    return extend(res, childVal)
              } else {
                    return res
              }
          }
      
    3. 合并watch

        // 观察者哈希不能覆盖一个
        // 另一个,所以我们将它们合并为数组。
      
        strats.watch = function (
          parentVal: ?Object,
          childVal: ?Object,
          vm?: Component,
          key: string
        ): ?Object {
          // work around Firefox's Object.prototype.watch...
          // 由于火狐浏览器Gecko内核实现了watch方法,这个地方需要重置watch值
          if (parentVal === nativeWatch) parentVal = undefined
          if (childVal === nativeWatch) childVal = undefined
      
          // 如果子类不存在,直接返回深拷贝的父类
          if (!childVal) return Object.create(parentVal || null)
      
          // 判断子类是否是对象,不是则报出警告
          if (process.env.NODE_ENV !== 'production') {
            assertObjectType(key, childVal, vm)
          }
      
          // 父类不存在,则直接返回子类
          if (!parentVal) return childVal
      
          const ret = {}
      
          // 将父类的属性浅拷贝
          extend(ret, parentVal)
      
          //
            // 遍历子类的watch,如果子类存在相同的监听对象,
            // 就将监听回调合并到一个数组中,便于后面顺序执行,
            // parent.concat(child)
            //
          for (const key in childVal) {
            let parent = ret[key]
            const child = childVal[key]
            if (parent && !Array.isArray(parent)) {
              parent = [parent]
            }
            ret[key] = parent
              ? parent.concat(child)
              : Array.isArray(child) ? child : [child]
          }
      
          // 返回合并后的数据
          return ret
        }
      
    4. 其他合并策略 props methods inject computed

        /**
          * Other object hashes.
          */
          strats.props =
          strats.methods =
          strats.inject =
          strats.computed = function (
            parentVal: ?Object,
            childVal: ?Object,
            vm?: Component,
            key: string
          ): ?Object {
            // 判断值是否是对象
            if (childVal && process.env.NODE_ENV !== 'production') {
              assertObjectType(key, childVal, vm)
            }
      
            // 父类没有值,直接返回子类
            if (!parentVal) return childVal
      
            // 将父类的值赋值到空对象中
            const ret = Object.create(null)
            extend(ret, parentVal)
      
            // 合并子类的值到ret中,子类覆盖父类
            if (childVal) extend(ret, childVal)
            return ret
          }
      
    5. 合并生命周期

        export const LIFECYCLE_HOOKS = [
          'beforeCreate',
          'created',
          'beforeMount',
          'mounted',
          'beforeUpdate',
          'updated',
          'beforeDestroy',
          'destroyed',
          'activated',
          'deactivated',
          'errorCaptured',
          'serverPrefetch'
        ]
      
        // 遍历hook钩子,循环添加hook合并策略
        LIFECYCLE_HOOKS.forEach(hook => {
          strats[hook] = mergeHook
        })
      
        /**
          * Hooks and props are merged as arrays.
          * 将对应的hook回调合并成一个数组
          */
          function mergeHook (
            parentVal: ?Array,
            childVal: ?Function | ?Array
          ): ?Array {
            const res = childVal
              ? parentVal
                ? parentVal.concat(childVal)
                : Array.isArray(childVal)
                  ? childVal
                  : [childVal]
              : parentVal
            return res
              ? dedupeHooks(res)
              : res
          }
      
          function dedupeHooks (hooks) {
            const res = []
            for (let i = 0; i < hooks.length; i++) {
              if (res.indexOf(hooks[i]) === -1) {
                res.push(hooks[i])
              }
            }
            return res
          }
      
  5. 初始化生命周期
    initLifecycle(vm)

  export function initLifecycle (vm: Component) {
    const options = vm.$options

    // locate first non-abstract parent
    // 查找第一个非抽象父级
    let parent = options.parent
    if (parent && !options.abstract) {
      // 如果有abstract属性,一直往上层寻找,直到不是抽象组件
      while (parent.$options.abstract && parent.$parent) {
        parent = parent.$parent
      }
      parent.$children.push(vm)
    }

    vm.$parent = parent
    vm.$root = parent ? parent.$root : vm

    vm.$children = []
    vm.$refs = {}

    vm._watcher = null
    vm._inactive = null
    vm._directInactive = false
    vm._isMounted = false
    vm._isDestroyed = false
    vm._isBeingDestroyed = false
  }

你可能感兴趣的:(vue源码分析-初始化操作)