-
Notifications
You must be signed in to change notification settings - Fork 0
/
vue3.js
167 lines (151 loc) · 4.25 KB
/
vue3.js
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
Vue3 实现响应式原理 主要使用了proxy
vue2 缺点: 使用了递归 数组改变长度无效 对象不存在的属性不能被拦截
*/
let toProxy = new WeakMap() // 原对象: 代理过的对象
let toRaw = new WeakMap() // 代理过的对象: 原对象
// 判断是不是对象
function isObject(val) {
return typeof val === 'object' && val !== null
}
// 判断是不是自己的属性
function hasOwn(target, key) {
return target.hasOwnProperty(key)
}
// 响应式
function reactive(target) {
return createReactiveObject(target)
}
// 创建响应式对象
function createReactiveObject(target) {
if (!isObject(target)) {
// 如果不是对象 直接返回
return target
}
let proxy = toProxy.get(target)
if (proxy) {
// 如果已经代理过了 直接返回代理的结果
return proxy
}
if (toRaw.has(target)) {
// 防止代理过的对象 被再次代理
return target
}
let baseHandler = {
// reflect 优点 不会报错 而且会有返回值 有object的所有方法
get(target, key, receiver) {
let result = Reflect.get(target, key, receiver)
// 搜集依赖 订阅 把当前的key 和effect 对应起来
track(target, key) // 如果目标上的key 变化了 重新让数组中的effect执行即可
// 如果有多层的时候 需要判断 进行递归操作
return isObject(result) ? reactive(result) : result
},
set(target, key, value, receiver) {
// 如何识别 是修改还是新增属性
let hasKey = hasOwn(target, key)
let oldValue = target[key]
let res = Reflect.set(target, key, value, receiver)
if (!hasKey) {
trigger(target, 'add', key)
} else if (oldValue !== value) {
// 为了屏蔽 无意义的更改
trigger(target, 'edit', key)
}
return res
},
deleteProperty(target, key) {
let res = Reflect.delete(target, key)
return res
}
}
let observed = new Proxy(target, baseHandler)
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
let activeEffectStacks = []
// 数据格式 如下
// { ------------------------- WeakMap
// target: { ------------------ Map
// key: [fn, fn, fn] ------------- Set
// }
// }
let targetsMap = new WeakMap()
// 追踪 如果目标上的key 变化了 重新让数组中的effect执行即可
function track(target, key) {
let effect = activeEffectStacks[activeEffectStacks.length - 1]
if (effect) {
// 如果存在对应关系
let depsMap = targetsMap.get(target)
if (!depsMap) {
targetsMap.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(key)
if (!deps) {
depsMap.set(key, (deps = new Set()))
}
if (!deps.has(effect)) {
deps.add(effect)
}
}
}
// 设置的时候自动触发
function trigger(target, type, key) {
let depsMap = targetsMap.get(target)
if (depsMap) {
let deps = depsMap.get(key)
if (deps) {
deps.forEach(effect => {
effect()
})
}
}
}
// 响应式 副作用
function effect(fn) {
// 需要把这个函数变成响应式的函数
let effect = createReactiveEffect(fn)
effect() // 默认先执行一次
}
// 响应式函数
function createReactiveEffect(fn) {
let effect = function() {
return run(effect, fn) // 运行 目的是: 1. 让fn执行 2. 把effect存到栈中
}
return effect
}
function run(effect, fn) {
// 运行fn 把effect存到栈中
try {
activeEffectStacks.push(effect)
fn()
} finally {
activeEffectStacks.pop()
}
}
// 依赖搜集 发布订阅
let obj = reactive({ name: 'xx', age: { n: 200 } })
effect(() => {
console.log(obj.age.n)
})
obj.age.n = 300
// console.log(obj.name)
// let obj = { name: 'xx', num: [1, 2, 3], x: { xx: 'xxx' } }
// let proxy = reactive(obj)
// // proxy.name = 'xxx'
// // proxy.age = 18
// // proxy.x.xx = 'xxxxxx'
// proxy.num.push(4)
// console.log('o', proxy.num)
// 需要记录一下 如果这个对象被代理过了 就不需要 new了 用map来存
// 比如 以下 如果没记录 就会多次代理
// let obj= {name: 'zx'}
// let proxy = reactive(obj)
// reactive(obj)
// reactive(obj)
// reactive(proxy)
// reactive(proxy)
// let arr = [1, 2, 3]
// let proxy = reactive(arr)
// // proxy.push(4)
// proxy.length = 100