MENU

Vue监测数据的原理

July 3, 2022 • vue

对象

简单模拟

const data = {
    name: "Jack",
    age: 19,
  };
  //创建一个监视的实例对象,用于监视data中属性的变化
  const obs = new Observer(data);

  //准备vm实例对象
  const vm = {};
  vm._data = data = obs;

  const Observer = function (obj) {
    //汇总对象中所有的属性
    const keys = Object.keys(obj);
    //遍历
    keys.forEach((k) => {
      //this指向当前实例对象obs
      Object.defineProperty(this, k, {
        get() {
          return obj[k];
        },
        set(val) {
          //修改了数据,进行解析-生成虚拟DOM等操作,来更新页面...
          obj[k] = val;
        },
      });
    });
  };

Vue.set()

在我们使用 vue 进行开发的过程中,可能会遇到一种情况:当生成 vue 实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去; 当我们去看 vue 文档的时候,会发现有这么一句话:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。 如下代码,给 student 对象新增 age 属性

data () {
  return {
    student: {
      name: '',
      sex: ''
    }
  }
}
mounted () { // ——钩子函数,实例挂载之后
  this.student.age = 24
}

一句话来说,就是在创建 vm 示例之后添加的属性,都不是响应性的,视图无法监听并更新。所以借用一个 API 来解决此类问题,即添加属性并且具备响应性Vue.set(target,key,val)
所以代码应该写为:Vue.set(vm.student,'age',22)
或者使用实例对象上的$set:vm.$set(vm.student,'age',22)
在 vm 对象内使用,this 指向 vm。但是,Vue.set 的 target 不允许是 vm 对象本身或者 vm._data

数组

数据类型为数组时,没有为 index 服务的 getter 和 setter,所以更改数组时候无法使用数组 + 索引值的方法来动态更新(可以修改)。但是可以通过 pop,push,unshift,shift,splice,sort,reverse 这些能修改原数组的方法来响应式地更新值。原因:包装,这些方法不是原生的 Array.prototype.push,而是 vue 自身包装的 push 方法。可以用 Vue.set 和 vm.$set 实现:Vue.set(this.arr,index,value)或者使用 splice 方法:this.arr.splice(index0,index1,'value')

总结

1.vue 会监视 data 中所有层次的数据

2.如何监测对象中的数据?

通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。对于后追加的属性默认不做响应式处理,请使用 Vue.set()或 vm.$set()

3.如何监测数据中的数据

通过包裹数组更新元素的方法实现,本质就是先调用原生对应方法对数组进行更新,然后重新解析模板进而更新页面

4.Vue 修改数组中的某个元素

使用修改数组的 7 个 API 或者 Vue.set() / vm.$set()

注意:Vue.set()和 vm.$set()不能给 vm 或 vm 根数据对象(vm._data)添加属性