美文网首页Vue技术探究Vue.js专区
Vue源码分析(2)--从一个实例开始

Vue源码分析(2)--从一个实例开始

作者: 风之化身呀 | 来源:发表于2017-07-08 16:40 被阅读167次

    前言

    本文是vue2.x源码分析的第二篇,主要讲解Vue初始化过程!

    1、贯穿全文的例子:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Vue</title>
      <script src="./vue.js" type="text/javascript" charset="utf-8" ></script>
    </head>
    <body>
      <div id="app">
        {{message}}
        <div v-text='abc' v-on:click='methods_a'></div>
      </div>
      <script>
        var app=new Vue({
          el:'#app',
          name:'app',
          mixins:{
            beforeCreate(){
              console.log('beforeCreate-1');
            }
          }
          props:['header-value'],
          data:{
            message:'hello'
          },
          beforeCreate(){
            console.log('beforeCreate-2'); 
          },
          computed:{
            abc(){
              return this.message+'s'
            }
          },
          components:{
            ABF:'abf'
          },
          directives:{
            v-focus:function some(){}
          },
          extends:{
            ext_a:'a'
          },
          methods:{
            methods_a:function(){
              alert('methods_a')
            }
          },
          watch:{
            'message':function(){
              console.log('message changed');
            },
            methods_a:function(){}
          }
        })
        // debugger;
        // setTimeout(()=>app.message='world',0)
      </script>
    </body>
    </html>
    

    这里先放一个简单的例子,随着分析的深入,会一步步将例子变复杂。分析的手段就是通过设置断点一步步看执行过程。

    2、new Vue({})执行过程分析

    • 首先找到Vue构造函数(启用IDE的查找功能能快速锁定)
    function Vue$3 (options) {
      if ("development" !== 'production' &&
        !(this instanceof Vue$3)) {
        warn('Vue is a constructor and should be called with the `new` keyword');
      }
      <!-- debugger; -->
      this._init(options); //程序主入口
    }
    
    • 打开上面代码的debugger注释来分析_init函数,该函数是通过initMixin(Vue)定义在Vue.prototype上的
    Vue.prototype._init = function (options) {
        var vm = this;
        // 每个vm实例都会有一个唯一的_uid属性
        vm._uid = uid++;
        // startTag, endTag 暂时不清楚干嘛的,不影响主流程,记为Unknown-2.1
        var startTag, endTag;
        /* istanbul ignore if */
        if ("development" !== 'production' && config.performance && mark) {
          startTag = "vue-perf-init:" + (vm._uid);
          endTag = "vue-perf-end:" + (vm._uid);
          mark(startTag);
        }
    
        // 设置_isVue标签避免被观测,记为Unknown-2.2
        vm._isVue = true;
        // 选项合并
        if (options && options._isComponent) {
          initInternalComponent(vm, options);
        } else {
          //主要执行过程-1(合并选项)
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          );
        }
        /* 设置代理 */
        {
          initProxy(vm);
        }
        // 将vm实例同时挂在_self属性上,记为Unknown-2.3
        vm._self = vm;
        // 主要执行过程-2(实例的初始化过程)
        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 ("development" !== 'production' && config.performance && mark) {
          vm._name = formatComponentName(vm, false);
          mark(endTag);
          measure(((vm._name) + " init"), startTag, endTag);
        }
    
        if (vm.$options.el) {
          // 主要执行过程-3(依赖收集、组件挂载等过程)
          vm.$mount(vm.$options.el);
        }
      };
    
    • 可以看到_init函数主要做了如下三件事,上述代码有标注
      • 主要执行过程-1(合并选项过程)
      • 主要执行过程-2(实例的初始化过程)
      • 主要执行过程-3(依赖收集、组件挂载等过程)
    • 这三件事后面会单独分析,除了这三件事,还有一个代理操作,来看看initProxy(vm)
    initProxy = function initProxy (vm) {
        //如果有本地Proxy类可用
        if (hasProxy) {
          // 判断用哪个代理处理对象
          var options = vm.$options;
          var handlers = options.render && options.render._withStripped
            ? getHandler
            : hasHandler;
          vm._renderProxy = new Proxy(vm, handlers);
        } else {
          vm._renderProxy = vm;
        }
    };
    

    这里大家可能对Proxy类比较陌生,可以先看看这里了解基本用法。其实就是给vm实例做一个hasHandler代理,并将代理对象挂在_renderProxy上,使得在访问vm._renderProxy对象上的属性时先调用has方法,返回值为真才去取该属性。这在后面的_render函数会用到

    3、小结

    • 下一步分析内容
      ☑ 主要执行过程-1(合并选项过程)
      ☐ 主要执行过程-2(实例的初始化过程)
      ☐ 主要执行过程-3(依赖收集、组件挂载等过程)
    • 遗留问题解决情况
      ☐ Unknown2.1
      ☐ Unknown2.2
      ☐ Unknown2.3

    相关文章

      网友评论

        本文标题:Vue源码分析(2)--从一个实例开始

        本文链接:https://www.haomeiwen.com/subject/qwcjhxtx.html