
在解決了鏈表節點指數增長的問題後,我們還需要注意依賴的有效性。
effect 的執行路徑可能因為條件判斷或程式邏輯不同而改變,導致這次執行中不再需要某些依賴。
如果這些過期依賴沒有被清理:
因此,在收集依賴時,不只要能正確複用,也必須具備 清理過期依賴 的機制。
下面我們來說需要清理依賴的兩個狀況。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
body {
padding: 150px;
}
</style>
</head>
<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 '../../../node_modules/vue/dist/vue.esm-browser.js'
import { ref, effect } from '../dist/reactivity.esm.js'
const flag = ref(true)
const name = ref('姓名')
const age = ref(18)
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 = '姓名' + Math.random()
}
ageBtn.onclick = () => {
age.value++
}
</script>
</body>
</html>
我們現在有三個變數,flag、name、age
flag 是 true,那點擊 name會觸發更新,但是點擊 age 不會觸發更新。flag 是 false,那點擊 age 會觸發更新,但是點擊 name 不會觸發更新。這很合理,因為沒有執行的狀況就不應該觸發。

effect: 1、 flag是 true。update flag:effect 更新,輸出 effect: 2、 flag是 false。update name:我們期望他沒有反應,因為「如果 flag 是 false,點擊 name 不會觸發更新。」effect: 3。我們先來看一下 effect 以及 name 裡面的內容,執行步驟是初始化後先點擊 update flag,再點擊 update name。
const e = effect(() => {
console.count('effect')
if(flag.value){
app.innerHTML = name.value
}else{
app.innerHTML = age.value
}
})
...
...
nameBtn.onclick = () => {
name.value = '姓名' + Math.random()
console.dir(e)
console.log(name)
}

我們預期 effect 回傳值中,不會有 name節點:實際上是正確的,目前 Link 節點是 flag → age。

我們預期 name 回傳值中,不會有頭節點跟尾節點,但實際上輸出結果是自己是正在被訂閱的狀態。
我們現在了解異常情況是
effect 裡面訂閱 flag、age 沒有 name,是我們要的正確結果。name 裡面發現自己被 effect 訂閱,但鏈表上沒有它節點,它應該要被清除。
遇到 flag 收集依賴,建立鏈表節點。

遇到 name,建立鏈表節點。

點擊按鈕,effect 重新執行,run 函式讓depsTail 指向undefined。

接著link函式建立鏈表節點,先檢查是否可以復用,頭節點desp存在depsTail == undefined,可以復用。

發現可以復用,depsTail 指向 link1,此時 flag 是 false。

由於 flag 是 false,接下來遇到 age,之前沒有收集依賴過,於是建立新的鏈表節點。

建立age鏈表節點之後,depsTail 的 nextDep 以及 depsTail 指向新節點。

此時你看黃底色的地方,發現 effect 已經沒有存 link2,但是 link2 的 sub 仍然指向 effect。
所以才會出現 name 更新,仍然會讓 effect 執行的情況。
const flag = ref(true)
const name = ref('姓名')
const age = ref(18)
let count = 0
effect(() => {
console.count('effect')
if(count>0) return
count++
if(flag.value){
app.innerHTML = name.value
}else{
app.innerHTML = age.value
}
})
flagBtn.onclick = () => {
flag.value = !flag.value
}
nameBtn.onclick = () => {
name.value = '姓名' + Math.random()
}
ageBtn.onclick = () => {
age.value++
}
到時候修正會一起調整,所以我們先來說第二種需要清除依賴的狀況。
count>0 會返回,無論如何點擊按鈕就不再觸發)count是 0,繼續往下執行
count++,count現在是1。flag.value → 收集依賴:flag。flag 是 true,接著讀到 name.value → 收集依賴:name。此時 Link 節點是
flag→name;depsTail指向name。
effect 被flag與name兩個 ref 訂閱。

run()。depsTail = undefined
effect 函式,馬上遇到 if (count > 0) return被中斷,因此鏈表節點上沒有任何改動。遇到 return 程式應該會中斷,可是你一直點擊 name按鈕,console.count('effect') 仍然一直觸發更新。

但遇到 if (count > 0) return,effect 沒有存取任何依賴, deps 鏈表上應該要是空的,可是鏈表上面有之前建立的鏈表沒有清除。
此時鏈表上的狀態一直處於:有頭節點deps,並且尾節點depsTail = undefined。
明天我們會來談怎麼清除這些依賴。
同步更新《嘿,日安!》技術部落格