# Vue3响应式实现原理
# 源码实现
/*
* @作者: xiangyulong
* @创建时间: 2020-05-14 17:11:30
* @最新更新时间: 2020-05-14 18:47:15
* @最新更新人: xiangyulong
* @描述:
*/
// 原始=》响应
let toProxy = new WeakMap()
// 响应=》原始
let toRaw = new WeakMap()
// 临时effect
let effectStack = []
// 保存依赖对象
let targetMap = new WeakMap()
const handle={
get(target,key){
let res = Reflect.get(target,key)
// 收集依赖
track(target,key)
// 为对象则递归
return typeof res === 'object'?reactive(res):res
},
set(target,key,val){
// 保存旧信息
const info = {oldVal:target[key],newVal:val}
let res = Reflect.set(target,key,val)
// 触发响应
trigger(target,key,info)
return res
}
}
// 拦截数据,生成响应式
function reactive(obj){
// 缓存中查找
let res = toProxy.get(obj)
if(res){
return res
}
if(toRaw.get(obj)){
return obj
}
// 未找到则进行响应式处理
res = new Proxy(obj,handle)
// 加入缓存
toProxy.set(obj,res)
toRaw.set(res,obj)
return res
}
// 收集依赖
function track(target,key){
// 获取当前effect
let effect = effectStack[effectStack.length-1]
if(effect){
let depMap = targetMap.get(target)
if(depMap===undefined){
// 对象不存在依赖则生成并保存
depMap = new Map()
targetMap.set(target,depMap)
}
let dep = depMap.get(key)
if(dep===undefined){
// key不存在初始化 set用于保存effect
dep = new Set()
depMap.set(key,dep)
}
// 判断当前effect是否存在,不存在则搜集
if(!dep.has(effect)){
dep.add(effect)
effect.deps.push(dep) // 双向保存
}
}
}
// 发布消息
function trigger(target,key,info){
let depMap = targetMap.get(target)
if(depMap===undefined){
return
}
if(key){
let deps = depMap.get(key)
let effects = new Set()
let computedRunner = new Set()
deps.forEach(effect=>{
if(effect.computed){
computedRunner.add(effect)
}else{
effects.add(effect)
}
})
console.log('吃')
// 执行
effects.forEach(i=>i())
computedRunner.forEach(i=>i())
}
}
// computed 特殊的effect
function computed(fn){
let res = effect(fn,{computed:true,lazy:true})
return {
effect: res,
get value(){
return res()
}
}
}
// effect事件
function effect(fn,options={}){
// 构造一个effect
const effect = createReactiveEffect(fn,options)
if(!options.lazy){
effect()
}
return effect
}
// 生成effect
function createReactiveEffect(fn,options){
// 定义创建effect
const effect = function effect(...args){
// 将其放入全局变量,方便收集依赖时获取
if(effectStack.indexOf(effect) === -1){
try{
effectStack.push(effect)
return fn(...args) // 此处获取属性会触发搜集依赖
}finally{
// 移除变量
effectStack.pop()
}
}
}
// 反向依赖保存依赖它的属性
effect.deps=[]
effect.computed = options.computed
effect.lazy = options.lazy
return effect
}
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149