Appearance
什么是依赖有效性呢
effect
的执行可能会因为条件判断或逻辑不同发生变化,导致部分依赖在执行过程中不再需要。 如果这些不再需要的依赖没有被正确移除,就会导致不必要的重新计算和性能问题。
- 内存泄漏: 不需要的链表节点一直存在
- 不必要的更新:
effect
不依赖某个ref
了, 但是这个ref
依然会触发effect
的重新执行
情况 1: 条件型依赖
html
<body>
<div id="app"></div>
<button id="flagBtn">update flag</button>
<button id="nameBtn">update name</button>
<button id="ageBtn">update age</button>
<script type="module">
import {ref, effect} from '../dist/reactivity.esm.js'
const flag = ref(true)
const name = ref('zhangsan')
const age = ref(12)
effect(() => {
console.count('effect')
if (flag.value) {
app.innerHTML = `姓名, ${name.value}!`
} else {
app.innerHTML = `变量, ${age.value}!`
}
})
flagBtn.onclick = () => {
flag.value = !flag.value
}
nameBtn.onclick = () => {
name.value = name.value + Math.random()
}
ageBtn.onclick = () => {
age.value = age.value + 1
}
</script>
</body>
目前 flag
、name
、age
三个变量都被 effect
收集为依赖,但实际上 name
与 age
是互斥的:
- 当
flag
为true
时,effect
只关心name
- 当
flag
为false
时,effect
只关心age
异常情况分析
effect
的依赖链表里已找不到name
节点,当前只剩flag → age
然而
name
的订阅链表里仍保留着指向该effect
的节点
异常结果: effect
的依赖链表中没有 name
节点, 但是 name
依赖链表中依然有 effect
节点
一步步拆解
步骤 | 示意图 | 说明 |
---|---|---|
① 首次执行 | ![]() | 收集 flag ,建立 link1 |
② 进入 if 分支 | ![]() | flag 为 true ,继续收集 name ,建立 link2 |
③ 点击按钮更新 flag | ![]() | effect 重新运行,depsTail 被重置为 undefined ,准备重新收集 |
④ 复用 flag 节点 | ![]() | 检查到 link1 可复用,depsTail 指向 link1 ,flag 现为 false |
⑤ 收集新依赖 age | ![]() | 进入 else 分支,新建 link3 并追加到 deps 尾部 |
根因
link2
在effect
的deps
链表中被移除, 但是在name
的subs
链表中依然存在name
依然会触发effect
的重新执行