第44题 执行new Vue干了什么

  • 当我们写下这段简单new Vue()代码,vue框架做了什么呢?
    var vm = new Vue({
       el:"#app",
         data:{
            msg:'this is msg'
         }
       }
    )
  • 调用src/core/instance/index.jsVue构造方法
    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')
      }
      //执行初始化方法,initMixin的时候,在vue原型上挂载了 _init方法
      this._init(options)
    }
    
    // 执行初始化的工作
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    export default Vue
  • 接下来调用原型上面_init方法,是我们要重点分析的,其入参options就是我们定义的对象时传入的参数对象
  • 执行内部初始化方法,首先是options的合并,之后是一堆init方法
  • options进行合并,vue会将相关的属性和方法都统一放到vm.$options中,为后续的调用做准备工作。vm.$option的属性来自两个方面,一个是Vue的构造函数(vm.constructor)预先定义的,一个是new Vue时传入的入参对象。合并完成后的options属性包括:

  • 初始化各类属性和事件

  • 挂载。如果说前面几部分都是准备阶段,那么这部分是整个new Vue的核心部分,将template编译成render表达式,然后转化为大名鼎鼎的Vnode,最终渲染为真实的dom节点

    // _init()的实现在src/core/instance/init.js中
    Vue.prototype._init = function (options) {
      // 第一部分 初始化属性
        var vm = this;
        // a uid
        vm._uid = uid$3++;
    
        var startTag, endTag;
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
          startTag = "vue-perf-start:" + (vm._uid);
          endTag = "vue-perf-end:" + (vm._uid);
          mark(startTag);
        }
    
        // a flag to avoid this being observed
        vm._isVue = true;
        // 第二部分 合并相关option merge options
        if (options && options._isComponent) {
          // optimize internal component instantiation
          // since dynamic options merging is pretty slow, and none of the
          // internal component options needs special treatment.
          initInternalComponent(vm, options);
        } else {
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          );
        }
        // 第三部分,初始化各类属性和事件
        /* istanbul ignore else */
        if (process.env.NODE_ENV !== 'production') {
          initProxy(vm);
        } else {
          vm._renderProxy = vm;
        }
        // expose real self
        vm._self = vm;
        initLifecycle(vm);
        initEvents(vm);
        initRender(vm);
        callHook(vm, 'beforeCreate');
        initInjections(vm); // resolve injections before data/props
        initState(vm);
        initProvide(vm); // resolve provide after data/props
        callHook(vm, 'created');
    
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
          vm._name = formatComponentName(vm, false);
          mark(endTag);
          measure(("vue " + (vm._name) + " init"), startTag, endTag);
        }
    
        if (vm.$options.el) {
          // 第四部分 挂载节点
          vm.$mount(vm.$options.el);
        }
      };

我们在内部能执行this.msg的原因是vm._data代理返回

    function initData (vm: Component) {
      let data = vm.$options.data
      data = vm._data = typeof data === 'function'
        ? getData(data, vm)
        : data || {}
      if (!isPlainObject(data)) {
        data = {}
        process.env.NODE_ENV !== 'production' && warn(
          'data functions should return an object:\n' +
          'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
          vm
        )
      }
      // proxy data on instance
      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 (process.env.NODE_ENV !== 'production') {
          if (methods && hasOwn(methods, key)) {
            warn(
              `Method "${key}" has already been defined as a data property.`,
              vm
            )
          }
        }
        if (props && hasOwn(props, key)) {
          process.env.NODE_ENV !== 'production' && warn(
            `The data property "${key}" is already declared as a prop. ` +
            `Use prop default value instead.`,
            vm
          )
        } else if (!isReserved(key)) {
          // 使我们能执行this.msg 
          proxy(vm, `_data`, key)
        }
      }
      // observe data
      observe(data, true /* asRootData */)
    }
    
    function proxy (target: Object, sourceKey: string, key: string) {
      sharedPropertyDefinition.get = function proxyGetter () {
        // 执行this.msg 被代理到this._data上面
        return this[sourceKey][key]
      }
      sharedPropertyDefinition.set = function proxySetter (val) {
        this[sourceKey][key] = val
      }
      Object.defineProperty(target, key, sharedPropertyDefinition)
    }

Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 datapropscomputedwatcher

Last Updated:
Contributors: leeguooooo