2-new Vue()

175 阅读3分钟

版本:Vue@2.6.10

new Vue的过程其实是调用了 Vue构造函数原型上的 _init方法(只分析初始化需要走的代码)

 Vue.prototype._init = function (options) {
    const vm = this
    vm._uid = uid++
    vm._isVue = true

      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )

    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    }else {
      vm._renderProxy = vm
    }

    vm._self = vm
    /*---------------------------------初始化过程------------------------------*/      

    /*---------------------------------生命周期初始化-----------*/
         initLifecycle(vm)
    //----------------------------------事件初始化-------------------------
      initEvents(vm)   
    //----------------------------------render初始化--------------------------
      initRender(vm)
    //调用beforeCreated钩子函数
      callHook(vm, 'beforeCreate')
    //初始化Injections
      initInjections(vm) //
    //初始化状态
      initState(vm)
    //初始化Provide
      initProvide(vm) // resolve provide after data/props
    //调用created钩子函数
      callHook(vm, 'created')
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

在 _init过程中,主要流程是:实例化Vue,为vm实例添加能力。

1-添加一些基础状态属性

vm._uid
vm._isVue = true
##mergeOptions过程
1-mergeOptions是将Vue构造函数中的options属性添加到实例上(增强vm功能)

resolveConstructorOptions(vm.constructor)的解析结果为:
Vue.options{
    components:{KeepAlive, Transition, TransitionGroup},
    directives:{model, show},
    filters:{},
    _base :Vue
}
mergeOptions函数将用户输入的props / inject / directives 进行规范化然后将Vue构造函数上的属性和用户options的属性
合并后添加到实例上。根据不同的属性有不同的合并策略。具体合并流程解析在(src\core\util\options.js)
##-- initProxy 过程
1-对vm进行代理,生成_renderProxy属性,在渲染时调用_renderProxy,根据拦截器给出友好提示。

2-合并配置项

vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options, vm)
//---------------------------------------------------------
1-resolveConstructorOptions(vm.constructor)
    function resolveConstructorOptions (Ctor) {
        var options = Ctor.options;
        if (Ctor.super) {
          var superOptions = resolveConstructorOptions(Ctor.super);
          var cachedSuperOptions = Ctor.superOptions;
          if (superOptions !== cachedSuperOptions) {
            // super option changed,
            // need to resolve new options.
            Ctor.superOptions = superOptions;
            // check if there are any late-modified/attached options (#4976)
            var modifiedOptions = resolveModifiedOptions(Ctor);
            // update base extend options
            if (modifiedOptions) {
              extend(Ctor.extendOptions, modifiedOptions);
            }
            options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);
            if (options.name) {
              options.components[options.name] = Ctor;
            }
          }
        }
        return options
      }
 1.1- 由于vm.constructor是Vue,Vue的最终结果是在第一节最后,初始Vue暂时没有super属性,所以返回
     Vue.options = {
         components:{KeepAlive, Transition, TransitionGroup},
         directive:{model, show},
         filter:{},
         _base :Vue
     }
2- mergeOptions(Vue.options, options, vm)
   篇幅较长,单独在2.1分析。

3-添加代理属性

vm._renderProxy
vm._self = vm
1- vm._renderProxy是对vm进行代理,在渲染获取数据的时候,有错时,给出提示。
2- vm._self引用自身。

4-初始化生命周期

function initLifecycle (vm) {
    var options = vm.$options;

    // locate first non-abstract parent
    var parent = options.parent;
    if (parent && !options.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;
  }
0-初始化状态信息,建立节点间的关系,保存根节点

5-初始化事件相关

 function initEvents (vm) {
    vm._events = Object.create(null);
    vm._hasHookEvent = false;
    // init parent attached events
    var listeners = vm.$options._parentListeners;
    if (listeners) {
      updateComponentListeners(vm, listeners);
    }
  }
0-与组件有关,初始化父子组件间的事件【在分析组件时,再用】

6-初始化渲染相关

 function initRender (vm) {
    vm._vnode = null; // the root of the child tree
    vm._staticTrees = null; // v-once cached trees
    var options = vm.$options;
    var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree
    var renderContext = parentVnode && parentVnode.context;
    vm.$slots = resolveSlots(options._renderChildren, renderContext);
    vm.$scopedSlots = emptyObject;
    // bind the createElement fn to this instance
    // so that we get proper render context inside it.
    // args order: tag, data, children, normalizationType, alwaysNormalize
    // internal version is used by render functions compiled from templates
    vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
    // normalization is always applied for the public version, used in
    // user-written render functions.
    vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };

    // $attrs & $listeners are exposed for easier HOC creation.
    // they need to be reactive so that HOCs using them are always updated
    var parentData = parentVnode && parentVnode.data;

    /* istanbul ignore else */
    {
      defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () {
        !isUpdatingChildComponent && warn("$attrs is readonly.", vm);
      }, true);
      defineReactive(vm, '$listeners', options._parentListeners || emptyObject, function () {
        !isUpdatingChildComponent && warn("$listeners is readonly.", vm);
      }, true);
    }
  }
0-与插槽,渲染函数,$attrs, $listeners有关。

7-调用[beforeCreate]生命周期钩子函数

 function callHook (vm, hook) {
    // #7573 disable dep collection when invoking lifecycle hooks
    pushTarget();
    var handlers = vm.$options[hook];
    var info = hook + " hook";
    if (handlers) {
      for (var i = 0, j = handlers.length; i < j; i++) {
        invokeWithErrorHandling(handlers[i], vm, null, vm, info);
      }
    }
    if (vm._hasHookEvent) {
      vm.$emit('hook:' + hook);
    }
    popTarget();
  }
0-调用生命周期钩子函数,如果父组件有监听子组件的生命周期,发射生命周期状态。

8-初始化[inject]属性

 function initInjections (vm) {
    var result = resolveInject(vm.$options.inject, vm);
    if (result) {
      toggleObserving(false);//非观测
      Object.keys(result).forEach(function (key) {
        /* istanbul ignore else */
        {
          defineReactive(vm, key, result[key], function () {
            warn(
              "Avoid mutating an injected value directly since the changes will be " +
              "overwritten whenever the provided component re-renders. " +
              "injection being mutated: \"" + key + "\"",
              vm
            );
          });
        }
      });
      toggleObserving(true);
    }
  }
function resolveInject (inject, vm) {
    if (inject) {
      // inject is :any because flow is not smart enough to figure out cached
      var result = Object.create(null);
      var keys = hasSymbol
        ? Reflect.ownKeys(inject)
        : Object.keys(inject);

      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        // #6574 in case the inject object is observed...
        if (key === '__ob__') { continue }
        var provideKey = inject[key].from;
        var source = vm;
        while (source) {
          if (source._provided && hasOwn(source._provided, provideKey)) {
            result[key] = source._provided[provideKey];
            break
          }
          source = source.$parent;
        }
        if (!source) {
          if ('default' in inject[key]) {
            var provideDefault = inject[key].default;
            result[key] = typeof provideDefault === 'function'
              ? provideDefault.call(vm)
              : provideDefault;
          } else {
            warn(("Injection \"" + key + "\" not found"), vm);
          }
        }
      }
      return result
    }
  }
0-需要与provide一起看,获取父级上的provid 提供的key,父级没有就接着向上级寻找,最终只有两种结果(找到 | 没   找到)
1-找到就直接赋值;
1.1-没找到,就看有没有default属性,有就使用,没有就告警
2- 将inject值赋值到vm上,且阻止改变inject。不对inject做观察赋能

9-初始化[state]状态属性

state包括props,methods,data,computed,watch属性,在【2.2-2.4

10-初始化[provide]状态属性

 function initProvide (vm) {
    var provide = vm.$options.provide;
    if (provide) {
      vm._provided = typeof provide === 'function'
        ? provide.call(vm)
        : provide;
    }
  }
0-需与inject一起看
1-provide提供数据,inject接收数据。
2-有provide,会将provide转换后存到_provide.

11-调用[created]生命周期钩子函数

与 7 相同

12-挂载

if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
## $mount 函数是在Vue的原型上(src\platforms\web\entry-runtime-with-compiler.js)
1-在挂载时,会根据render,template,el 确定挂载的内容
   -根据el属性,获取el属性对应的Element(1-el是string类型,根据id获取dom,2-el是其他类型,直接返回el), 	  
    在初始化渲染时,我们会有一个渲染的优先顺序,render属性为主,当没有render属性,寻找template属性,		
    template如果是字符串且以'#'开头,就根据'#id',去找到id对应的dom属性,获取innerHTML当作			   
    template,否则template可能是Element,是Element的话直接获取Element的innerHTML当作模板,都不是的话,告警,
    没有template的话,根据el属性(此时的el属性已经是Element)获取outerHTML作为template,然后根据我们获取的template属性,
    对template进行编译(compiler在单独一个文件里'./compiler')【后续解析】
   -总结:
	 也就是当有render属性时就不需要对template所对应的值进行编译了,没有render,也是将template对应的值转换成
         render进行挂载。
2-在对template进行编译后生成render或者本来就有有render,对render进行挂载
   -运行 mount.call(this, el, hydrating)进行挂载

3- mount函数是缓存的[公用]的Vue.prototype.$mount方法
   -在这个方法中,实际是调用的mountComponent(this,el,hydrating)//非服务端渲染情况下hydrating为false

mountComponent

mountComponent 方法在初始化渲染阶段使用,在组件渲染阶段同样使用,我们现在只分析初始化渲染,因此会对 mountComponent 进行简化。

function mountComponent (
    vm,
    el,
    hydrating  //非服务端渲染为false
  ) {
    vm.$el = el;
    callHook(vm, 'beforeMount');
    var updateComponent = function () {
        vm._update(vm._render(), hydrating);
      };
    new Watcher(vm, updateComponent, noop, {
      before: function before () {
        if (vm._isMounted && !vm._isDestroyed) {
          callHook(vm, 'beforeUpdate');
        }
      }
    }, true /* isRenderWatcher */);
    hydrating = false;
    if (vm.$vnode == null) {
      vm._isMounted = true;
      callHook(vm, 'mounted');
    }
    return vm
  }

1-保存el
2-调用'beforeMount'钩子函数
3-生成 updateComponent 
3-new Watcher() 渲染watcher,进行渲染 [渲染watcher,和其他两个一起分析]
4-根据调用时间,判断是mounted 还是 update
5-将实例返回