Vue2/3 中响应式的区别
2024-01-28 19:46:39
众所周知,Vue2 中的响应式是通过 Object.defineProperty
来实现的,而 Vue3 中是通过 ES6 的 Proxy 来实现的
Vue2
我们先来看下 Vue2 是如何做的
ts
const obj = {
a: 1,
b: 2,
c: {
d: 3,
e: 4,
},
}
function observe(obj) {
for (let key in obj) {
let v = obj[key]
if (typeof v === 'object' && v !== null) {
observe(v)
}
Object.defineProperty(obj, key, {
get() {
console.log('get', key)
return v
},
set(val) {
if (val !== v) {
console.log('set', key)
v = val
}
},
})
}
}
observe(obj)
obj.c = 1
delete obj.b
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
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
本质上就是封装了一个 observe 函数,将对象的每个 key 进行监听 getter 和 setter,这样就可以获取到对象的访问和赋值,在里面插入想要进行的操作,并且判断属性值是否是对象,如果还是对象的话就进行递归
那么这样的做法就有一个天生的缺陷,由于要对每个属性进行监听,所以进行了深度遍历,造成了性能问题,并且在执行 observe 函数时对传入的对象进行深度遍历完成了属性监听,也就是说当前对象有的属性都被监听到了,但是在这之后对这个对象添加的属性就监听不到了,因为此时监听的步骤已经结束了,在 vue 的生命周期 created 之前就完成了
TIP
在使用 delete
关键字删除对象的属性时也触发不到监听
Vue3
那么我们看在 Vue3 里是如何做的
ts
const obj = {
a: 1,
b: 2,
c: {
d: 3,
e: 4,
},
}
function observe(obj) {
const proxy = new Proxy(obj, {
get(target, key) {
let v = target[key]
if (typeof v === 'object' && v !== null) {
v = observe(v)
}
console.log('get', key)
return v
},
set(target, key, val) {
if (target[key] !== val) {
console.log('set', key)
target[key] = val
}
},
deleteProperty(target, key) {
console.log('delete', key)
delete target[key]
},
})
return proxy
}
const proxy = observe(obj)
proxy.c = 1
delete proxy.b
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
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
使用 Proxy 就不会再有前面的问题了,不用再管对象有多少个属性,因为 Proxy 监听的是整个对象。但是我们在看代码的时候第 15 行不也是进行了递归调用吗?为什么就不会有这个问题呢?和前面 Vue2 对比你会发现,使用 Proxy 的递归,是只有在访问到这个属性的时候才会去进行深度遍历,而 Vue2 是在初始的时候就会对整个对象的所有属性进行监听,这两者的区别是很大的,而对象添加新属性时对象是能够收到通知的
并且通过 Proxy 的 deleteProperty
我们是可以监听的对象的删除操作的