Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Vue的依赖收集之引用类型 #95

@andyChenAn

Description

@andyChenAn

Vue的依赖收集之引用类型

如果依赖收集过程中,数据是引用类型,那么在设置响应式的时候,会有所不同。会分为两种,一种是对象,一种是数组。

举个例子:

<div id="app">
  <div id="box">{{person.name}} = {{person.age}}</div>
</div>

<script>
  let vm = new Vue({
    el: "#app",
    data: {
      person : {
        name : 'andy',
        age : 234
      }
    }
  });
</script>

对象

如果数据是对象,那么会遍历对象所有属性,给属性设置响应式,如果属性是基本类型,那么跟依赖收集之基本类型的流程是一样的,每个数据对象都会添加一个__ob__属性。

__ob__下面会有一个dep属性,其实就是一个Dep对象,用来收集依赖的,为什么数据对象需要通过__ob__.dep来收集依赖呢?那是因为对象数据有可能会在其他地方被使用,比如说Vue.set或者Vue.del方法,举个例子:

<div id="app">
  <div id="box">{{person.name}} - {{person.age}}-{{person.address}}</div>
</div>

<script>
  let vm = new Vue({
    el: "#app",
    data: {
      person : {
        name : 'andy',
        age : 234
      }
    },
    created () {
      setTimeout(() => {
        this.$set(this.person , 'address' , '广州');
      } , 2000)
    }
  });
</script>

上面代码中,当2秒后,会给person对象添加一个address属性,但是我们一开始初始化的时候并没有添加address属性,如果我们直接使用this.person.address="广州"页面是不会更新的,因为person对象下的address属性没有设置响应式,那么这个时候我们如果需要给person对象添加新的属性并且设置属性为响应式的话,我们就需要使用vm.$set方法来实现,而vm.$set方法内部就是使用了数据对象__ob__.dep来通知依赖更新的。所以我们需要在数据对象中添加一个__ob__.dep来收集依赖。

基本类型和引用类型收集依赖过程中,主要区别在于:基本类型只使用闭包dep来收集依赖,而引用类型是使用闭包dep和__ob__.dep两种来收集依赖。闭包dep只是存在于defineReactive方法中,其他地方根本访问不到。而对象数据就有可能会在其他地方被使用,就像vm.set方法。

function defineReactive$$1(obj, key, val, customSetter, shallow) {
    // 创建一个闭包dep,用来收集依赖
    var dep = new Dep()
    // 如果val是一个对象,那么会返回一个ob对象
    var childOb = !shallow && 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();
          // 如果是一个对象,那么ob.dep也会收集依赖
          if (childOb) {
            childOb.dep.depend();
            // 如果是数组,那么就会遍历数组,主要是为了数组下面的子元素是对象,给对象也保存一份依赖
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
        }
        return value;
      },
      // 省略代码...
    });
}

数组

如果数据是一个数组,那么会遍历数组,如果数组中的子元素是对象,那么就按照对象的方式进行处理,和对象的处理方式一样,如果是数组的话,也会给数组保存一个__ob__属性,数组中的__ob__.dep也是用来收集依赖的,作用和对象中的__ob__.dep一样。

数组为什么需要__ob__属性呢?因为Vue内部重写了数组中的一些方法,而__ob__dep中保存的依赖就是给这些方法使用的。

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methodsToPatch = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
methodsToPatch.forEach(function (method) {
    var original = arrayProto[method];
    def(arrayMethods, method, function mutator() {
      var args = [],
        len = arguments.length;
      while (len--) args[len] = arguments[len];
    
      var result = original.apply(this, args);
      var ob = this.__ob__;
      var inserted;
      switch (method) {
        case "push":
        case "unshift":
          inserted = args;
          break;
        case "splice":
          inserted = args.slice(2);
          break;
      }
      if (inserted) {
        ob.observeArray(inserted);
      }
      // 通知依赖更新
      ob.dep.notify();
      return result;
    });
});

上面代码中,当我们使用数组的上述方法时,数组发生了改变,所以会通知依赖这个数组的watcher进行更新。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions