美文网首页Vue技术探究Vue.js专区
Vue源码分析(4)--实例的初始化过程

Vue源码分析(4)--实例的初始化过程

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

    前言

    本文是vue2.x源码分析的第四篇,主要讲解vue实例的初始化过程init*系列!

    先看调用形式

        initLifecycle(vm);
        initEvents(vm);
        initRender(vm);
        callHook(vm, 'beforeCreate');
        initInjections(vm); // 在data/props之前处理注入,暂不清楚作用Unknown4.1,本节不分析
        initState(vm);      //最主要的函数
        initProvide(vm);    // 在data/props之后处理provide,暂不清楚作用Unknown4.2,本节不分析
        callHook(vm, 'created');
    

    1、分析initLifecycle(vm),initEvents(vm),initRender(vm)

    1.1、initLifecycle(vm)

    function initLifecycle (vm) {
      var options = vm.$options;
      // 定位第一个非抽象的parent,记为Unknown4.1
      var parent = options.parent;
      if (parent && !options.abstract) {
        while (parent.$options.abstract && parent.$parent) {
          parent = parent.$parent;
        }
        parent.$children.push(vm);
      }
      //给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;
    }
    

    1.2、initEvents(vm)

    function initEvents (vm) {
      vm._events = Object.create(null);
      vm._hasHookEvent = false;
      // 处理父元素中的events,Unknown4.2
      var listeners = vm.$options._parentListeners;
      if (listeners) {
        updateComponentListeners(vm, listeners);
      }
    }
    

    1.3、initRender(vm)

    function initRender (vm) {
      vm.$vnode = null; // the placeholder node in parent tree,在父树中的位置
      vm._vnode = null; // the root of the child tree,子树的根
      vm._staticTrees = null;//静态树
      var parentVnode = vm.$options._parentVnode;
      var renderContext = parentVnode && parentVnode.context;
      vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext);//处理slot
      vm.$scopedSlots = emptyObject;
      // 将createElement函数绑定到该实例,参数顺序:tag, data, children, normalizationType, alwaysNormalize.
      vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };//内部调用版
      vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };外部调用版
    }
    

    之后,beforeCreate生命周期函数被调用:callHook(vm, 'beforeCreate');

    2、initState(vm)

    function initState (vm) {
      vm._watchers = [];                         //用于存放所用的watcher实例
      var opts = vm.$options;
      if (opts.props) {
        initProps(vm, opts.props);               //对props各项进行验证
      }
      if (opts.methods) {
        initMethods(vm, opts.methods);           //将methods中各项添加到vm上
      }
      if (opts.data) {
        initData(vm);                            //对data进行观测
      } else {
        observe(vm._data = {}, true /* asRootData */);
      }
      if (opts.computed) {
        initComputed(vm, opts.computed);         //将computed属性添加到vm,并定义响应式
      }
      if (opts.watch) {
        initWatch(vm, opts.watch);               //对watch属性进行处理...
      }
    }
    

    2.1、initProps(vm,propsOptions)

    function initProps (vm, propsOptions) {
      var propsData = vm.$options.propsData || {};
      var props = vm._props = {};
      // 对props中key进行缓存以减少遍历
      var keys = vm.$options._propKeys = [];
      var isRoot = !vm.$parent; //是否是根节点
      // 根实例的props需要被转换
      observerState.shouldConvert = isRoot; //observeState={isSettingProps:false,shouldConvert:false}
      var loop = function ( key ) {
        keys.push(key);
        var value = validateProp(key, propsOptions, propsData, vm);
        {
          if (isReservedProp[key]) {  //判断是否是保留的prop(不能是key,slot,ref)
            warn(
              ("\"" + key + "\" is a reserved attribute and cannot be used as component prop."),
              vm
            );
          }
          //将props的key定义为响应式,这里的匿名函数是作为customSetter用的,defineReactive$$1函数后面会分析
          defineReactive$$1(props, key, value, function () {
            if (vm.$parent && !observerState.isSettingProps) {
              warn(
                "Avoid mutating a prop directly since the value will be " +
                "overwritten whenever the parent component re-renders. " +
                "Instead, use a data or computed property based on the prop's " +
                "value. Prop being mutated: \"" + key + "\"",
                vm
              );
            }
          });
        }
        /* 静态props在Vue.extend()时已经被代理,这里只需对实例上的props进行代理,代理的目的很简单,
        就是使props中的属性可以用vm.xxx访问,而不必vm.props.xxx,实现很简单,就是使用API:Object.defineProperty*/
        if (!(key in vm)) {
          proxy(vm, "_props", key);
        }
      };
      for (var key in propsOptions) loop( key );
      observerState.shouldConvert = true;
    }
    

    主要分析下validateProp(key, propsOptions, propsData, vm)

    //该函数就是对prop进行验证,如type、default、required、validator
    function validateProp (
      key,
      propOptions,
      propsData,
      vm
    ) {
      var prop = propOptions[key];
      var absent = !hasOwn(propsData, key);
      var value = propsData[key];
      // 处理布尔类型的prop
      if (isType(Boolean, prop.type)) {
        if (absent && !hasOwn(prop, 'default')) {
          value = false;
        } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {
          value = true;
        }
      }
      if (value === undefined) {
        value = getPropDefaultValue(vm, prop, key);//当type为object时,default必须是函数
        // 观测value
        var prevShouldConvert = observerState.shouldConvert;
        observerState.shouldConvert = true;
        observe(value);
        observerState.shouldConvert = prevShouldConvert;
      }
      {
        assertProp(prop, key, value, vm, absent);//检查是否有required和validator
      }
      return value
    }
    

    2.2、initMethods(vm, opts.methods)

    function initMethods (vm, methods) {
      var props = vm.$options.props;
      for (var key in methods) {
        //遍历methods,将methods中的方法挂在vm实例上,注意这里调用了bind函数,故methods中所有方法的this都是vm对象
        vm[key] = methods[key] == null ? noop : bind(methods[key], vm);
        {
          if (methods[key] == null) {
            warn(
              "method \"" + key + "\" has an undefined value in the component definition. " +
              "Did you reference the function correctly?",
              vm
            );
          }
          //检测props中是否与methods中有同名属性
          if (props && hasOwn(props, key)) {
            warn(
              ("method \"" + key + "\" has already been defined as a prop."),
              vm
            );
          }
        }
      }
    }
    

    2.3、initData(vm)

    function initData (vm) {
      //这里的data通过策略合并对象变成了函数mergedInstanceDataFn
      var data = vm.$options.data;
      data = vm._data = typeof data === 'function'
        ? getData(data, vm) //getData就是执行mergedInstanceDataFn函数,返回data对象
        : data || {};
      //当data函数返回的不是对象时
      if (!isPlainObject(data)) {
        data = {};
        "development" !== 'production' && warn(
          'data functions should return an object:\n' +
          'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
          vm
        );
      }
      // 在实例vm上代理data中各项,作用同props的代理
      var keys = Object.keys(data);
      var props = vm.$options.props;
      var i = keys.length;
      while (i--) {
        //data中属性不能和props中同名
        if (props && hasOwn(props, keys[i])) {
          "development" !== 'production' && warn(
            "The data property \"" + (keys[i]) + "\" is already declared as a prop. " +
            "Use prop default value instead.",
            vm
          );
        } else if (!isReserved(keys[i])) { //data中属性不能以_或$开头
          proxy(vm, "_data", keys[i]);
        }
      }
      // 对data进行观测
      observe(data, true /* asRootData */);
    }
    

    这里主要分析observe(data, true)

    //给value添加一个observer,保存在value.__ob__属性上。
    function observe (value, asRootData) {
      //不对普通类型观测
      if (!isObject(value)) {
        return
      }
      var ob;
      if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__;
      } else if (
        observerState.shouldConvert &&
        !isServerRendering() &&
        (Array.isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&
        !value._isVue //当有_isVue属性时,该value不会被观测
      ) {
        ob = new Observer(value); //主要函数,实例化一个Observer
      }
      if (asRootData && ob) {
        ob.vmCount++;
      }
      return ob
    }
    

    来看看Observer(value)

    var Observer = function Observer (value) {
      this.value = value;
      this.dep = new Dep();  //每个observer都实例化一个Dep,用于收集依赖
      this.vmCount = 0;
      def(value, '__ob__', this); //将__ob__放在value上,值为observer对象
      if (Array.isArray(value)) { //当value是数组
        var augment = hasProto
          ? protoAugment
          : copyAugment;
        augment(value, arrayMethods, arrayKeys);
        this.observeArray(value);
      } else {
        this.walk(value);          //当value是对象
      }
    };
    

    来看看walk(value)

    Observer.prototype.walk = function walk (obj) {
      var keys = Object.keys(obj); //这里__ob__不会出现在keys里
      for (var i = 0; i < keys.length; i++) {
        defineReactive$$1(obj, keys[i], obj[keys[i]]); //对obj中每一项进行响应式定义
      }
    };
    

    来看看defineReactive$$1(obj, keys[i], obj[keys[i]])

    function defineReactive$$1 (obj,key,val,customSetter) {
      var dep = new Dep();
      var property = Object.getOwnPropertyDescriptor(obj, key);
      if (property && property.configurable === false) {
        return
      }
      // 引用预先定义的getter/setters
      var getter = property && property.get;
      var setter = property && property.set;
     //val可能是对象,故继续观测
      var childOb = observe(val);
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          var value = getter ? getter.call(obj) : val;
          if (Dep.target) {
            dep.depend();
            //子对象也收集父对象的依赖
            if (childOb) {
              childOb.dep.depend();
            }
            //对数组的依赖处理
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          var value = getter ? getter.call(obj) : val;
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
          if ("development" !== 'production' && customSetter) {
            customSetter();//报错用
          }
          if (setter) {
            setter.call(obj, newVal);
          } else {
            val = newVal;
          }
          childOb = observe(newVal); //设置新值后,对新值进行观测
          dep.notify();  //触发观测器的回调或get函数
        }
      });
    }
    

    2.4、initComputed(vm, opts.computed)

    function initComputed (vm, computed) {
      var watchers = vm._computedWatchers = Object.create(null);//computed中watcher存放在vm._computedWatchers
      for (var key in computed) {
        var userDef = computed[key];
        var getter = typeof userDef === 'function' ? userDef : userDef.get;
        {
          if (getter === undefined) {
            warn(
              ("No getter function has been defined for computed property \"" + key + "\"."),
              vm
            );
            getter = noop;
          }
        }
        // 对computed中每一个属性创建Watcher.
        watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);
        // 将computed中的属性定义到vm上,跟代理差不多,使vm.xx等价于vm.computed.xx
        if (!(key in vm)) { //computed中属性若和vm上同名会被vm上的覆盖
          defineComputed(vm, key, userDef);
        }
      }
    }
    

    来看看Watcher(vm, getter, noop, computedWatcherOptions)

    var Watcher = function Watcher (vm,expOrFn,cb,options) {
      this.vm = vm;
      vm._watchers.push(this);      //vm._watchers存放所有的watcher实例
      // 处理options,在initComputed中,options={lazy:true},即lazy watchers;
      // 在initWatch中,options={user:true}
      if (options) {
        this.deep = !!options.deep;
        this.user = !!options.user;
        this.lazy = !!options.lazy;
        this.sync = !!options.sync;
      } else {
        this.deep = this.user = this.lazy = this.sync = false;
      }
      this.cb = cb;      //存放回调函数cb
      this.id = ++uid$2; // uid for batching
      this.active = true;
      this.dirty = this.lazy; // for lazy watchers
      this.deps = [];
      this.newDeps = [];
      this.depIds = new _Set();
      this.newDepIds = new _Set();
      this.expression = expOrFn.toString();  //存放传入的expOrFn
      if (typeof expOrFn === 'function') {
        // 当expOrFn是函数,initComputed从这儿走
        this.getter = expOrFn;
      } else {
        // 当expOrFn是字符串,initWatch从这儿走
        this.getter = parsePath(expOrFn);//字符串若有分隔,只能用'.'号,不能用空格,'-'等
        if (!this.getter) {
          this.getter = function () {};
          "development" !== 'production' && warn(
            "Failed watching path: \"" + expOrFn + "\" " +
            'Watcher only accepts simple dot-delimited paths. ' +
            'For full control, use a function instead.',
            vm
          );
        }
      }
      this.value = this.lazy  //initComputed中lazy是true,故不调用get函数;initWatcher中会调用get
        ? undefined
        : this.get();
    };
    

    2.4、initWatch(vm, opts.watch)

    function initWatch (vm, watch) {
      for (var key in watch) {
        var handler = watch[key];
        if (Array.isArray(handler)) { //当handler是数组时,分别调用createWatcher
          for (var i = 0; i < handler.length; i++) {
            createWatcher(vm, key, handler[i]);
          }
        } else {
          createWatcher(vm, key, handler);
        }
      }
    }
    

    来看看createWatcher(vm, key, handler)

    function createWatcher (vm, key, handler) {
      var options;
      if (isPlainObject(handler)) {  //当handler是数组时应该提供handler属性
        options = handler;
        handler = handler.handler;
      }
      if (typeof handler === 'string') {   //当handler是字符串,从vm上找同名属性
        handler = vm[handler];
      }
      vm.$watch(key, handler, options);  //主要函数
    }
    

    来看看vm.$watch(key,handler,options)

    Vue.prototype.$watch = function (expOrFn,cb,options) {
        var vm = this;
        options = options || {};
        options.user = true;  //user表示是否是用户自定义的watcher
        var watcher = new Watcher(vm, expOrFn, cb, options); //回到2.3中再看一遍,会发现最后调用get函数
        if (options.immediate) {
          cb.call(vm, watcher.value);
        }
        return function unwatchFn () {  //返回解除watcher的函数
          watcher.teardown();
        }
    };
    

    来看看get函数

    Watcher.prototype.get = function get () {
      pushTarget(this);//内部执行了Dep.target = this,并将this存入栈targetStack;
      var value;
      var vm = this.vm;
      if (this.user) {
        try {
          value = this.getter.call(vm, vm);//调用getter,在initWatch中即调用如下函数:
          // function (obj) {  //这里的obj=vm
          //   for (var i = 0; i < segments.length; i++) {  //segments是对观测表达式进行的分割
          //     if (!obj) { return }
          //     obj = obj[segments[i]]; //当segments.length>1时,第一次从vm上找同名属性返回,后面就从obj上找,例如
          //                             //当观测表达式是'article.title',那么segments=['article','title']
          //                             //先找vm.article,再在article上找title作为结果返回
          //   }
          //   return obj
          // }
        } catch (e) {
          handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
        }
      } else {
        value = this.getter.call(vm, vm);
      }
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {  //是否进行深度观测
        traverse(value);
      }
      popTarget();      //从targetStack栈中弹出this,将Deep.target仍为this
      this.cleanupDeps();  //暂不清楚,Unknown4.1
      return value
    };
    

    当initState调用完成后,created生命周期函数被调用:callHook(vm, 'created');

    3、小结

      由于还没到渲染环节,故本节中定义的响应式属性还不能起作用,不太好理解,下一节将会看到
    这些响应式属性是怎么起作用的

    相关文章

      网友评论

        本文标题:Vue源码分析(4)--实例的初始化过程

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