深究 Vue 响应式原理

时间:2021-1-8 作者:admin

Vue 的响应式原理好像挺神奇的,但是仔细观察,不用看源码也能明白其中的原理。

一、Vue 对数据做了什么?

首先我们来做一个小实验,当我们正常打印一个含有一个属性为 n 的对象时,类如 {n:1},可以看到控制台的打印结果是:

现在我们将这个对象放在 Vue 构造选项的 data 内,代码如下所示:

const myData = {
  n:1
}
const vm = new Vue({
  data:{
    myData
  },
  render: h => h(App)
}).$mount("#app");

console.log(vm.myData)

可以看到控制台的打印结果是:

可以发现,一个简简单单的对象经过 Vue 之后就变得比较复杂了,多了对原来属性 n 的 getter 和 setter,那么 Vue 又是怎样实现数据响应式的呢?先从 getter 和 setter 讲起。

二、getter 和 setter

getter 和 setter 是 ES6 新增的语法,按照官方文档说,get语法将对象属性绑定到查询该属性时将被调用的函数,当尝试设置属性时,set语法将对象属性绑定到要调用的函数。简单点,当你读取一个对象属性时就会调用 get 函数,修改时调用 set 函数,调用这两个函数不需要像普通函数那样加圆括号实现调用,如下所示:

const obj = {
  _n:1,
  get n (){
    return  this._n
  },
  set n(newValue){
    this._n = newValue
  }
}
console.log(obj.n)  //相当于调用 get 函数
obj.n = 10  //相当于调用 set 函数
console.log(obj.n)
console.log(obj._n)

使用 getter 和 setter 的时候不能在函数中返回或设置本身,这样很容易引起循环引用问题。设置了 getter 和 setter 函数的对象打印结果如下:

这样的打印结果和 Vue 处理后的打印结果是不是很相似呢,但是仅仅通过这些还是不能实现数据响应式,我们还得明白一件事,那就是数据代理。

三、数据代理

什么是数据代理?简单点说就是把对一个对象的操作(如读写操作)放在另一个对象上,我们只能操作另一个对象来间接改变本来的对象,而这另一个对象就是代理,类比我们租房的时候,本来是应该跟房东谈,但是房东全权交给房产中介代为管理,那么我们就只能去找房产中介了,看下列代码:

const myData = {
  n: 1
};

function proxy(data) {
  let obj = {};
  Object.defineProperty(obj, "n", {
    get() {
        return data.n;
      },
      set(newValue) {
        data.n = newValue;
      }
  });
  return obj;
}
let obj = proxy(myData);
console.log(obj.n);
obj.n = 10;
console.log(myData);

该代码就实现了把对 myData 的读写放在了 obj 上,当我们访问 obj.n 的时候实际上返回的是 myData 的 n ,当我们设置 obj.n 的时候实际上设置的是 myData 的值,这样就实现了代理,但是你可能会问:如果仅仅是上述逻辑的话,我们也可以不通过代理,直接修改 myData 不就可以完全跳过代理吗?这样的确会完全跳过代理,所以我们需要进一步完善:

const myData = {
  n: 1
};

function proxy(data) {
  let _value=data.n;
  let obj = {};
  Object.defineProperty(data, "n", {
    get() {
        return _value;
      },
      set(newValue) {
        _value = newValue;
      }
  });

  Object.defineProperty(obj, "n", {
    get() {
        return data.n;
      },
      set(newValue) {
        data.n = newValue;
      }
  });
  let obj = proxy(myData);
  return obj;
}

通过在代理函数中同时加上对原有对象的 getter 和 setter 函数,这样只要通过了代理,不论怎样修改对象的属性,我们都可以监测到,设想一下在上面的 set 函数内加上 render 函数呢,只要用户修改了这个值,就立刻调用 render 函数实现页面渲染,这样一来不就是 Vue 的数据响应式了嘛。

Vue 的数据响应式其实还更加复杂,本文只是简单深究了一下,若要深入研究,建议可以去看一下 Vue 的源码。

小生不才,希望各位多多指教!

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。