Vue响应式实现原理

Vue的一大特性就是响应式。这里的响应式指的是,当状态发生变化时,系统会自动更新关联状态。在Vue中的具体表现有:当数据发生改变时,触发视图重新渲染;computed属性在依赖值发生变化时,自动重新计算新值;提供watch监听器,可以监听到数据的变化。

这些都是怎么实现的呢?Vue2和Vue3中关于响应式实现的原理不太一样,Vue2使用ES5的defineProperty实现,而Vue3使用的是ES6的propxy.(PS:这也就是为什么Vue2不支持IE7/8,而Vue3不支持IE11.)

defineProperty实现

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法:

Object.defineProperty(obj, prop, descriptor)

  1. 第一个参数obj:要设置属性的对象;
  2. 第二个参数prop:要设置的属性名,这个属性可以是已存在也可以是不存在的;
  3. 第三个参数descriptor:该参数接收一个对象,用来对属性进行描述。如value(值),writable(是否可重写),enumerable(是否可枚举)等

举个?:

const student = {};

Object.defineProperty(student, 'age', {
  value: 17,
  writable: true
});

student.age = 18;

console.log(student.age);//打印出18
复制代码

这个例子里,定义了一个student对象。然后通过defineProperty给该对象定义了age属性,该属性值是可写的。所以,后面我们可以修改这个student对象age的值。

那之所以能够用来它实现响应式,是因为它的第三个参数,还提供了getter和setter方法。

get: 属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。该函数的返回值会被用作属性的值。默认为 undefined。

set: 属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。默认为 undefined。

举个?:

let student = {};
let age;

Object.defineProperty(student,'age',{
  get:function() {
    console.log('读取age');
    return age;
  },
  set:function(val) {
    console.log('设置age');
    age = val;
  }
});
复制代码

当对age进行设置和读值时:

image.png

也就是说,有了getter和setter,当某个属性被读取和设置时,我们可以进行拦截并做一些事情(比如重新渲染页面)。

如果我们想让对象的所有属性都具有响应式,就需要对全部属性进行遍历,实现getter和setter:

function convert (obj) {
  Object.keys(obj).forEach(key => {
    let internalValue = obj[key]
    Object.defineProperty(obj, key, {
      get () {
        console.log(`读取"${key}": ${internalValue}`)
        return internalValue
      },
      set (newValue) {
        console.log(`设置"${key}"为: ${newValue}`)
        internalValue = newValue
      }
    })
  })
}
复制代码

再进一步,如果对象的某个属性的值是一个数组或者对象,那么就还需要进行深度的遍历。

  function convert(obj) {
    Object.keys(obj).forEach((key) => {
      let internalValue = obj[key]
      //Object.defineProperty()...
      if (typeof internalValue === "object") {
        convert(internalValue);
      }
    });
  }
复制代码

以上就是通过defineProperty实现响应式的主要原理。这种方法存在一个不足之处就是对于对象新增加的属性,仍然不具备响应式的特定。

Proxy实现

Vue3使用Proxy来实现响应式。先来看看MDN上的定义:

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

语法:

const p = new Proxy(target, handler)

1.第一个参数target:要包装的目标对象;
2.第二个参数handle:接收一个对象,内部定义了操作目标对象时的方法;

通过给对象设置代理,我们可以拦截对象属性的取值/赋值操作。

举个?:

  const student = {
    age: 23,
  };
  const handler = {
    get(target, prop) {
      console.log("读值:", prop);
      return target[prop];
    },
    set(target, key, value) {
      console.log("设置值", key, value);
      target[key] = value;
      return true;
    },
  };
  const proxy = new Proxy(student, handler);
  console.log(proxy.age);
复制代码

执行结果:

image.png

总结

通可见,虽然Vue3使用了ES6的新特性,但是基本思路还是跟Vue2一样的:通过拦截属性的取赋值进行数据的追踪与监听,从而实现数据变化触发页面的重新渲染。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享