跳到主内容

Vue3响应式原理

· 3分钟阅读

Vue3

在 Vue3 之前,vue 针对对象的属性监听主要通过 Object.defineProperty 来实现,通过拦截对象属性的 get/set 方法,在渲染页面的时候会触发 get 方法,创建一个 watcher 实例 来收集依赖,当 set 方法触发,通过 watcher 来通知相应的组件重新渲染。然而这种方法只适用于对象,对数组是通过重写数组原型链上的 push,pop,shift,unshift,splice,sort,reverse 方法,可以通过源码查看 methodsToPatch,实现细节也很简单,直接看源码

methodsToPatch.forEach(function (method) {
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args); // 直接调用数组方法
const ob = this.__ob__; // 获取当前的数组已经绑定的 __ob__ 对象
let 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;
});
});

vue2 的实现不足之处

  1. 无法对对象新增或者删除的属性进行响应式处理
  2. 无法对数组下标修改进行响应式处理

于是为了更换解决历史问题,引入了 Proxy对象 来重新响应式逻辑

Proxy 是什么

Proxy 中文翻译为代理,意思是可以通过 Proxy 创建一个代理对象,他可以拦截对象/数组的 set/get 方法,举个栗子,我们通过 Proxy 代理一个数组

const arr = [1, 2, 3];
const p = new Proxy(arr, {
get(target, key) {
console.log(`get ${target} key ${key}`);
return target[key];
},
set(target, key, value) {
console.log(`set ${target} key ${key} value ${value}`);
},
});

这时候直接调用 p.push(4) ,控制台打印结果如下,可以说明通过调用 push 方法会触发 set 方法

p.push(4)

那么如果直接修改下标 1 的数据会这样呢?我们来试试 p[1] = 99 ,控制台可以看出,直接通过下标修改也会触发 set 方法,由此可见,vue2 的缺点得到了解决,牛逼

p[1] = 99

总结

通过 Proxy 代理对象来拦截对象和数组方法这手段实在是高,不过这也限制了 Vue3 现在只能在现代浏览器上访问