
一文了解Vue3的响应式原理
- 一、🟩回顾Object.defineProperty
- 二、🟨Proxy基本使用
- 三、🟦学习Proxy语法:Reflect
- 四、🟧Vue3如何用Proxy实现响应式
- 1、实现响应式
- 2、Proxy总结
- (1)深度监听,性能更好
- (2)可监听 新增/删除 属性
- (3)可监听数组变化
- 3、两者对比
- 五、🟪结束语
之前写过一篇文章谈论 vue2.x 的响应式原理,但因为 vue3 也来了,紧跟着 vue3 的步伐,周一开始学起了 vue3 的响应式原理。
大家应该都听过, vue3 用 proxy 来解决响应式原理,同时它解决了 vue2 中 Object.definePropery 存在的一些问题,但同时也带来了一些问题。
在下面的这篇文章中,将讲解关于 vue3 用 proxy 如何实现响应式,以及带来的一些问题。一起来学习吧💯
一、🟩回顾Object.defineProperty
这里需要大家对 ObjectProperty 的知识点有一个预先了解,如有需要了解可点击文章进行查看~
现在,我们来回顾下 Object.defineProperty 的缺点:
- 深度监听时需要一次性递归;
- 无法监听新增属性/删除属性(需要配合
Vue.set或Vue.delete使用); - 无法原生监听数组,需要特殊处理。
带着 Object.defineProperty 的这几个缺点,接下来我们开始进入 Proxy 的世界。
二、🟨Proxy基本使用
下面用一段代码来演示 Proxy 的基本使用。具体代码如下:
const data = {name: 'monday',age:18
}
//const data = ['a', 'b', 'c']const proxyData = new Proxy(data, {get(target, key, receiver){//只处理本身(非原型的)属性const ownKeys = Reflect.ownKeys(target)if(oenKeys.includes(key)){console.log('get', key) //监听}const result = Reflect.get(target, key, receiver)return result // 返回结果},set(target, key, val, receiver){//重复的数据,不处理if(val === target[key]){return true}const result = Reflect.set(target, key, val, receiver)console.log('set', key, val)//console.log('result', result) //truereturn result // 是否设置成功},deleteProperty(target, key){const result = Reflect.deleteProperty(target, key)console.log('delete property', key)return result // 是否删除成功}
})
通过以上代码可得,我们先定义一个对象字面量的 data ,之后在作为 Proxy 实例化的参数进行传递。且 proxyData 实现了 get 、 set 和 deleteProperty 的方法,可以对数据进行增删改操作。
三、🟦学习Proxy语法:Reflect
我们再来认识 Proxy 的一个好朋友,Reflect。
Reflect 对象有着和 Proxy 一一对应的能力,Reflect对象一共有 13 个静态方法,这也就是我们平常所听到的 proxy 有多达13种拦截行为,而 Reflect 的这13种静态方法匹配的就是 Proxy 的13种拦截行为 。
| 静态方法列表 |
|---|
| Reflect.get(target, name, receiver) |
| Reflect.set(target, name, value, receiver) |
| Reflect.has(obj, name) |
| Reflect.deleteProperty(obj, name) |
| Reflect.construct(target, args) |
| Reflect.getPrototypeOf(obj) |
| Reflect.setPrototypeOf(obj, newProto) |
| Reflect.apply(func, thisArg, args) |
| Reflect.defineProperty(target, propertyKey, attribute) |
| Reflect.getOwnPropertyDescriptor(target, propertyKey) |
| Reflect.isExtensible(target) |
| Reflect.preventExtensions(target) |
| Reflect.ownKeys(target) |
Reflect 的出现是为了替换掉 Object 上的工具函数,这里不做具体介绍,详情可查看文档 。
四、🟧Vue3如何用Proxy实现响应式
1、实现响应式
下面来实现Proxy的响应式。附上代码:
// 创建响应式
function reactive(target = {}) {if (typeof target !== 'object' || target == null) {// 不是对象或数组,则返回return target}// 代理配置const proxyConf = {get(target, key, receiver) {// 只处理本身(非原型的)属性const ownKeys = Reflect.ownKeys(target)if (ownKeys.includes(key)) {console.log('get', key) // 监听}const result = Reflect.get(target, key, receiver)// 深度监听return reactive(result)},set(target, key, val, receiver) {// 重复的数据,不处理if (val === target[key]) {return true}const ownKeys = Reflect.ownKeys(target)// 判断是已有属性还是新增属性if (ownKeys.includes(key)) {console.log('已有的 key', key)} else {console.log('新增的 key', key)} const result = Reflect.set(target, key, val, receiver)console.log('set', key, val)// console.log('result', result) // truereturn result // 是否设置成功},deleteProperty(target, key) {const result = Reflect.deleteProperty(target, key)console.log('delete property', key)// console.log('result', result) // truereturn result // 是否删除成功}}// 生成代理对象const observed = new Proxy(target, proxyConf)return observed
}// 测试数据
const data = {name: 'monday',age: 18,info: {city: 'FuZhou',a: {b: {c: {d: {e: 100}}}}}
}const proxyData = reactive(data)
我们在控制台来验证数据:

从上图中可以看到,用 proxy 来实现响应式,如果遇到需要深度递归的数组时,它不会像 defineProperty 那样深度递归,它会在什么时候 get ,什么时候再深度递归。本质上来讲就是,你获取到哪一层,那一层才会触发响应式。你获取不到的深层,它就不会触发响应式。且从代码中我们可以了解到, Proxy 在修改属性时,如果数据是重复的,则不进行处理。如果数据不重复,再进行处理。这样一来,就极大程度上提高了软件的性能。
2、Proxy总结
现在来对上述Proxy的内容做一个总结:
(1)深度监听,性能更好
defineProperty 是一次性递归完成;而 Proxy 是什么时候 get ,什么时候再深度递归。
(2)可监听 新增/删除 属性
在 vue2 中, defineProperty 是无法新增/删除属性的,需要配合 Vue.set 和 Vue.delete 来使用,而在 Vue3 中, Proxy 可以新增和删除属性,无需进行特殊处理。
(3)可监听数组变化
在 vue2 中,监听数组变化是需要进行特殊处理,且只能一次性深度递归完成。而在 vue3 中,可以监听数组变化,并且是什么时候get什么时候再递归,获取不到的深层,不会触发响应式。
3、两者对比
讲到这里,我们再把 vue2 中的 Object.defineProperty 和 vue3 中的 Proxy 做一个对比:
Proxy能良好的规避Object.defineProperty的问题;Proxy无法兼容所有浏览器(如IE11),且无法polyfill。
五、🟪结束语
从某种程度上来说, vue3 的 Proxy 确实带来了一些好处,但同时也带来了一些问题。正因为如此, vue2 的 Object.defineProperty 还会存在很长一段时间。所以,新技术的使用总会经过一个从试用阶段到稳定阶段的过程。
关于vue3的响应式原理讲到这里就结束啦!如有疑问或文章有误欢迎评论区留言或私信交流~
- 关注公众号 星期一研究室 ,第一时间关注技术干货,更多有趣的专栏待你解锁~
- 如果这篇文章对你有用,记得 一键三连 再走哦!
- 我们下期见!🥂🥂🥂