Vue2、Vue3响应式原理

Object.defineProperty

Vue2响应式基于Object.defineProperty

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
const target = {
name:'cny',
age:22
}

function reactive(obj,key,value){
Object.defineProperty(target,key,{
get(){
return value
},
set(newval){
if(newval !== value){
return value = newval
}
}
})
}
Object.keys(target).forEach(key => reactive(target,key,target[key]))

console.log(target.name)
console.log(target.name = 'cny2')

//但target新增属性进行重新访问和设值,都不会再触发get\set方法,Object.defineProperty支队初始对象的属性具有监听作用,新增属性失效 数组只能操作七种方法,修改某一项值无法劫持
//解决方法 Vue.$set(target,'fruit','apple')
target.fruit = 'apple'
target.fruit = 'banana'

Vue3响应式基于Proxy

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
46
47
48
49
50
51
52
53
54
55
const target = {
name:'cny',
age:22
}

function reactive(target){
const handler = {
get(target,key,receiver){
return Reflect.get(target,key,receiver)
//return Reflect.get(...arguments)
},
set(target,key,value,receiver){
return Reflect.set(target,key,value,receiver)
}
}
return new Proxy(target,handler)
}

const proxyTarget = reactive(target)

console.log(proxyTarget.name)
console.log(proxyTarget.name = 'cny2')

proxyTarget.fruit = 'apple'
console.log(proxyTarget.fruit)

let activeEffect;
function effect(fn) {
const _effect = function () {
activeEffect = _effect
fn()
}
_effect()
}

const targetMap = new WeakMap()
function track(target, key) {
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let deps = depsMap.get(key)
if (!deps) {
deps = new Set()
depsMap.set(key, deps)
}
deps.add(activeEffect)
}

function trigger(target, key) {
const depsMap = targetMap.get(target)
const deps = depsMap.get(key)
deps.forEach(effect => effect())
}

通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、添加、删除等

通过Reflect(反射):对源对象的属性进行操作

receiver:它保证传递正确的this给getter或者setter

Reflect会提供一个安全的提示程序来转发操作,并确保我们不会忘记与此相关的任何内容。