Vue3的底层实现-响应式数据的Proxy

介绍

相对于Vue2的defineProperty的实现,Vue3采用Proxy的实现。defineProperty为静态方法因此无法监听新增属性/删除属性,并且无法直接监听数组。

主要通过Reflect的四个方法操作语言内部的属性,Reflect的原型就是Object。

Reflect.get(目标对象,属性名,上下文对象) – 读取对象属性

Reflect.set(目标属性,属性名,属性值,上下文对象)-- 设置对象属性

Reflect.deleteProperty(目标对象,属性名)-- 删除对象属性

Reflect.ownKeys(目标对象)-- 返回由目标对象自身的属性(只处理本身-非原型的属性)组成的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 创建响应式
function reactive(target) {
if (typeof target !== 'object' || typeof target == null) {
return target // 不是对象或数组直接返回
}
// 代理配置
const proxyConf = {
~ get(targe, 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('set 已有的属性', key) // 监听
} else {
console.log('set 新增的属性', key)
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // 是否设置成功
},

deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('deleteProperty', key)
return result
}
}

// 生成代理 对象
const observed = new Proxy(target, proxyConf)
return observed;
}

例如获取对象 a 中属性 d 的值:

1
2
3
4
5
6
7
8
9
const a = {
b: {
c: {
d: 'proxy'
}
}
}
let ref_a = reactive(a)
console.log(ref_a.b.c.d)

get 方法总共会调用3次,即进行三次递归,直到下一次 get 的不是对象或者数组。
与definePropety的区别是,proxy只有我们读取到对象深层次的属性时,才会触发递归,而definePropety的深度监听会一次性监听所有数据。