Vue2 源码解析-响应式系统 data部分

158 阅读4分钟

1.初始化部分

   function Vue(options) {
    if (!(this instanceof Vue)) {
      warn("Vue is a constructor and should be called with the `new` keyword");
    }
    this._init(options);
  }
  
   Vue.prototype._init = function (options) {
       ...
       initLifecycle(vm);
      initEvents(vm);
      initRender(vm);
      callHook(vm, "beforeCreate");
      initInjections(vm); 
      initState(vm); //主要部分
      initProvide(vm);
      callHook(vm, "created");
      ...
   }
   
  function initState(vm) {
    vm._watchers = [];
    var opts = vm.$options;
    if (opts.props) {
      initProps(vm, opts.props);
    }
    if (opts.methods) {
      initMethods(vm, opts.methods);
    }
    if (opts.data) {
      initData(vm);
    } else {
      observe((vm._data = {}), true /* asRootData */);
    }
    if (opts.computed) {
      initComputed(vm, opts.computed);
    }
    if (opts.watch && opts.watch !== nativeWatch) {
      initWatch(vm, opts.watch);
    }
  }

2.Data初始化

   function initData(vm) {
    var data = vm.$options.data;
    //首先获取了组件的 data 选项,如果 data 是一个函数,则调用该函数获取数据对象
    data = vm._data =
      typeof data === "function" ? getData(data, vm) : data || {};
      // getData -> data.call(vm, vm)
      // data 不是一个纯对象-通过{}或new创建的对象,则会发出警告
    if (!isPlainObject(data)) { //toString.call(data) === "[object Object]"
      data = {};
      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
    var keys = Object.keys(data);
    var props = vm.$options.props;
    var methods = vm.$options.methods;
    var i = keys.length;
    while (i--) {
      var key = keys[i];
      // 判断是否和method以及props命名冲突
      {
        if (methods && hasOwn(methods, key)) {
          warn(
            'Method "' + key + '" has already been defined as a data property.',
            vm
          );
        }
      }
      if (props && hasOwn(props, key)) {
        warn(
          'The data property "' +
            key +
            '" is already declared as a prop. ' +
            "Use prop default value instead.",
          vm
        );
      } else if (!isReserved(key)) {
     // 遍历 vm._data 中的每个属性,并使用 proxy 函数将它们代理到 vm 上
     // 可以通过 vm.propertyName 访问 vm._data.propertyName
        proxy(vm, "_data", key);
      }
    }
    // 使用 observe 函数观察数据对象,以便在数据发生更改时能够捕获更改
    observe(data, true /* asRootData */);
  }

3.观察者

该部分用于创建一个观察者实例用于监听对象发生变化,来达到数据变化的时候触发视图的更新

  function observe(value, asRootData) {
    if (!isObject(value) || value instanceof VNode) {
    // 检查传入的值是否是一个对象,如果不是或者这个值是一个 VNode,就直接返回
      return;
    }
    var ob;
    if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    // 检查是否已经有了一个观察者实例,如果有的话就直接返回这个实例
      ob = value.__ob__;
    } else if (
      shouldObserve && // 是否观察 
      !isServerRendering() && //服务端渲染
      (Array.isArray(value) || isPlainObject(value)) && //是对象或者数组
      Object.isExtensible(value) && //对象可扩展
      !value._isVue //当前不是vue实例
    ) {
      ob = new Observer(value);
    }
    if (asRootData && ob) {
      ob.vmCount++;
    }
    return ob;
  }
  
  function Observer(value) {
    this.value = value; // 观察的对象
    this.dep = new Dep();// 创建一个新的 Dep 对象,该对象用于收集依赖
    this.vmCount = 0;
    def(value, "__ob__", this); // 标记当前值是Observer实例
    // 判断是否数组
    if (Array.isArray(value)) {
    // 判断是否有__proto__属性
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      // 改写数组原型 push/pop等七个方法 如果调用这些方法会触发dep.notify以达到试图更新的效果
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  };
  
  function walk(obj) {
    //如果是对象进行遍历 对每个属性定义getter和setter
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i]);
    }
  };
  
  function defineReactive(obj, key, val, customSetter, shallow) {
    var dep = new Dep();
    ...
     Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter() {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {// 防止多次添加Watcher
          dep.depend(); // 此处为添加Watcher 需结合Watcher部分一起看后面会再提到这部分
          ...
        }
        return value;
      },
      set: function reactiveSetter(newVal) {
        var value = getter ? getter.call(obj) : val;
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return;
        }
       ...
        if (getter && !setter) {
          return;
        }
        if (setter) {
          setter.call(obj, newVal);
        } else {
          val = newVal;
        }
        dep.notify(); //  数据变化时 遍历执行之前depend的Watcher 
        //需结合Watcher部分一起看后面会再提到这部分
      },
    });
    

4.Watcher部分

   updateComponent = function () {
   // 此处为更新试图的实现 
  // vm.render生成虚拟节点 其中涉及获取data中的数据从而触发之前观察者部分中对象属性的getter函数
        vm._update(vm._render(), hydrating);
     };
     // 渲染Wachter
   new Watcher(
      vm,
      updateComponent,
      noop,
      {
        before: function before() {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, "beforeUpdate");
          }
        },
      },
      true /* isRenderWatcher */
    );
   
  var Watcher = function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
    this.vm = vm;
    ... //此处省略代码,仅显示关键内容
    if (typeof expOrFn === "function") {
      this.getter = expOrFn; // 将前面更新试图的实现赋值给getter
    } 
    this.value = this.lazy ? undefined : this.get(); // 调用get() (lazy为计算属性用到的watcher)
  };
  
  function get() {
    pushTarget(this);
    // pushTarget的实现如下 结合之前getter函数的if(Dep.target)
    //function pushTarget(target) {
    //  targetStack.push(target);
    //  Dep.target = target; //target为当前Watcher
    // }
    var value;
    var vm = this.vm;
    try {
      value = this.getter.call(vm, vm); //此处调用后导致触发之前观察者部分中对象属性的getter函数
    } catch (e) {
      ...
    } finally {
     ...
      popTarget(); // 此处为删除 目的是防止每次访问数据都进行依赖收集
      this.cleanupDeps();
    }
    return value;
  };
  
  // 然后回到getter函数中的dep.depend()
   Dep.prototype.depend = function depend() {
    if (Dep.target) {
      Dep.target.addDep(this); //此处实际调用的是Watcher中的addDep函数 此处this是dep实例
    }
  };
  
   Watcher.prototype.addDep = function addDep(dep) {
    var id = dep.id;
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id);
      this.newDeps.push(dep);
      if (!this.depIds.has(id)) {
        dep.addSub(this); // 此处实际调用的dep原型上的addSub函数 此处this为Watcher实例
      }
    }
  };
  
  var Dep = function Dep() {
  // 该构造函数在之前遍历data的对象属性时会执行
    this.id = uid++;
    this.subs = [];
  };
  
  Dep.prototype.addSub = function addSub(sub) {
    this.subs.push(sub); 
    // subs为执行构造函数Dep是的属性 
  };
  
  // 所以绕了一圈 vue最终达到的效果是为每个遍历的属性添加Watcher实例
  
   Dep.prototype.notify = function notify() {
    // stabilize the subscriber list first
    var subs = this.subs.slice();
    if (!config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort(function (a, b) {
        return a.id - b.id;
      });
    }
    for (var i = 0, l = subs.length; i < l; i++) {
      subs[i].update(); 
    }
  };
   Watcher.prototype.update = function update() {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true;
    } else if (this.sync) {
      this.run();
    } else {
      queueWatcher(this); // 将watcher推入队列
      //queueWatcher -> nextTick(flushSchedulerQueue); 会在下一次dom更新时执行,期间经过很多操作,最终会调用watcher的run方法
    }
  };
     */
  Watcher.prototype.run = function run() {
    if (this.active) {
      var value = this.get(); // 完成试图的更新
     }
     ...
  }