背景:
之前写了通过理解 Object.defineProperty
用法去理解 vue2
响应式原理的文章。所以就最近抽空写一篇通过理解Proxy
代理去理解 vue3
响应式 的文章,而且通过学会底层实现方式去对比 vue2
和 vue3
响应式变化肯定理解上会更直观。下面将从基本介绍和用法去写。
附带vue2
响应式原理理解地址:通过学会Object.defineProperty的用法理解vue2响应式原理地址
一、Proxy的作用简述:
Proxy
是 ES6
中新增的一个函数,也可以叫类, Proxy
本质就是一种构造器,所以我们要通过 new
去实现的。Proxy
的作用是一种拦截器,即在设置对象属性或者读取对象属性时做一层拦截操作。
先说一个注意事项:Proxy没有原型对象
由于Proxy
构造出来的实例对象是对目标对象的一个代理,因此 Proxy
在构造过程中是不需要 prototype
进行初始化的,因为其他构造函数之所以需要 prototype
,是因为构造出来的对象需要一些初始化的成员,所以将这些成员定义到了 protoype
上,Proxy
只是用来代理其他对象,所以本身不需要prototype
。
二、Proxy具体用法:
基本语法是:
new Proxy(target, handler)
;
其中:
- 第一个参数
target
为被代理对象- 第二个参数
handler
为做拦截需要做什么操作即set、get
等方法。
其中第二个参数包含众多方法,例如:
handler.getPrototypeOf() // 在读取代理对象的原型时触发该操作
handler.setPrototypeOf() // 在设置代理对象的原型时触发该操作
handler.isExtensible() // 在判断一个代理对象是否是可扩展时触发该操作
handler.preventExtensions() // 在让一个代理对象不可扩展时触发该操作
handler.getOwnPropertyDescriptor() // 在获取代理对象某个属性的属性描述时触发该操作
andler.defineProperty() // 在定义代理对象某个属性时的属性描述时触发该操作
handler.has() // 在判断代理对象是否拥有某个属性时触发该操作
handler.get() // 在读取代理对象的某个属性时触发该操作
handler.set() // 在给代理对象的某个属性赋值时触发该操作
handler.deleteProperty() // 在删除代理对象的某个属性时触发该操作
handler.ownKeys() // 在获取代理对象的所有属性键时触发该操作
handler.apply() // 在调用一个目标对象为函数的代理对象时触发该操作
handler.construct() // 在给一个目标对象为构造函数的代理对象构造实例时触发该操作
重点来了,我们理解vue3响应式原理思路主要通过get和set就行:
get
方法有三个参数get(target, key, proxy)
分别为目标对象、键、实例对象本身
set
方法有四个参数 set(target, key, value, proxy)
,分别为目标对象、键、值、实例对象本身。
(1)看看get
如何拦截属性访问
const target = { name: '天天鸭' };
const handler = {
get: function (target, prop) {
console.log("get天天鸭");
if (prop in target) {
return target[prop];
} else {
return 'Property does not exist';
}
}
};
const test = new Proxy(target, handler);
test.name // 当获取任何对象属性内容时, 打印 "get天天鸭"
主要留意最后一行代码, 当获取任何对象属性内容时,都会触发get
方法.
(2)看看set
如何拦截属性修改
const target = { name: '天天鸭' };
const handler = {
set: function (target, prop, value) {
console.log('触发了set');
target[prop] = value;
return true;
}
};
const test = new Proxy(target, handler);
test.name = '天天鸭2'; // 只要做任何修改属性的行为会打印 '触发了set'
主要留意最后一行代码, 做任何修改属性的行为都会 触发了set
方法
小结一下:
结合如上代码例子所示,通过认识get、set
方法就知道proxy
是对整个对象进行了拦截,只要获取里面任何一个属性内容就会触发 get
方法,设置对象任何一个属性就触发 set
方法。这样就能通过它知道什么时候应该更新视图,结合依赖收集机制实现了vue3
的响应式。
最终对比 vue2
的 Object.defineProperty
是通过递归去拦截对象内的所有属性,而Proxy
是对整个完整对象进行拦截的,因此解决了 vue2
对数组下标无法监听的问题,这也正是 vue3
使用 Proxy
而放弃了 Object.defineProperty
的根本原因。