-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy path依赖追踪和注销逻辑梳理外部使用.html
139 lines (128 loc) · 4.37 KB
/
依赖追踪和注销逻辑梳理外部使用.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>
依赖追踪和注销逻辑梳理
</h1>
<div>
依赖追踪的api <a href="https://v3.cn.vuejs.org/api/effect-scope.html#effectscope">effectScope</a> 其实已经被暴露出来供用户使用
</div>
<div>
这个api的本质就是为了更方便的管理副作用的收集和销毁。在源码内部使用的也是当前api
</div>
<div>
为什么要有effectScope?
</div>
<div>
具体请看vuerfc 的 <a
href="https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md"></a> rc
</div>
<script src="../packages/vue/dist/vue.global.js"></script>
<script>
//具体实现逻辑实例:
let activeEffectScope
const effectScopeStack = []
class EffectScope {
// active 的作用是为了防止执行stop销毁之再次执行当前实例的方法依然生效
active = true
effects = []
cleanups = []
parent
scopes
index
constructor(detached = false) {
if (!detached && activeEffectScope) {
this.parent = activeEffectScope
this.index =
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
this
) - 1
}
}
run(fn) {
if (this.active) {
try {
this.on()
return fn()
} finally {
this.off()
}
}
}
// 依赖追踪使用
on() {
if (this.active) {
effectScopeStack.push(this)
activeEffectScope = this
}
}
off() {
if (this.active) {
effectScopeStack.pop()
activeEffectScope = effectScopeStack[effectScopeStack.length - 1]
}
}
stop(fromParent) {
if (this.active) {
this.effects.forEach(e => e.stop())
this.cleanups.forEach(cleanup => cleanup())
if (this.scopes) {
this.scopes.forEach(e => e.stop(true))
}
this.active = false
}
}
}
// 建立关联的函数,当前函数在computed、watch、watchEffect等函数中使用
function recordEffectScope(
effect,
scope
) {
scope = scope || activeEffectScope
if (scope && scope.active) {
scope.effects.push(effect)
}
}
//1、 在外部使用
// 由于源码中recordEffectScope 被保存在vue 的对象中无法获取
//为了能模拟源码中调用recordEffectScope 方法重写 computed
// 2、挡在内部使用时,其实就是收集了组件的依赖,从而能达到在卸载时,统一执行scope.stop统一卸载当前组件的的依赖的所有effect
const computed = Vue.computed
Vue.computed = function (fn) {
const val = computed(fn)
// 模拟拿到effect
recordEffectScope(val.effect)
return val
}
const ref = Vue.ref
Vue.ref = function (val) {
const newRef = computed(val)
// 模拟拿到effect
recordEffectScope(val.effect)
return newRef
}
function effectScope() {
return new EffectScope()
}
const scope = effectScope()
const num = Vue.ref(0)
const val = scope.run(() => {
//这个情况下是没有依赖收集的
const doubled = Vue.computed(() => {
console.log(1111)
return num.value * 2
})
return '他还能有返回值'
})
num.value++
// 卸载了所有的依赖
scope.stop()
</script>
</body>
</html>