iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
Vue.js

從零到一打造 Vue3 響應式系統系列 第 10

Day 10 - Effect:為何會被指數級觸發?

  • 分享至 

  • xImage
  •  

banner

DOM 互動

我們的響應式系統經過前幾天的努力,已經做得差不多,感覺可以加上一下 DOM 的互動,來進行簡單的測試。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <style>
      body {
        padding: 150px;
      }
    </style>
  </head>
  <body>
    <button id="btn">按鈕</button>
    <script type="module">
      import { ref, effect } from '../dist/reactivity.esm.js'

      const flag = ref(true)

      effect(() => {
        flag.value
        console.count('effect')
      })

      btn.onclick = () => {
        flag.value = !flag.value
      }
    </script>
  </body>
</html>

day10-01

看起來不太妙,目前 effect 出現按鈕指數觸發,這樣一定是不行。
我們預期每次點擊按鈕,effect 只會執行一次。但實際情況看起來不太妙。

console.count 的結果可以看到,effect 的執行次數隨著點擊呈現指數級增長。

我們來了解一下問題癥結點。

建立 Link 節點問題癥結點

執行步驟圖解

初始化頁面

day10-06

頁面載入時,effect 執行一次。在執行過程中,讀取了 flag.value,觸發 getter 進行依賴收集。
系統會建立一個 link1 節點,將 effectflag 關聯起來。到這裡都符合預期。

點擊第一次按鈕

day10-02

當按鈕第一次被點擊,flag.valuetrue 變為 false,觸發了 setter。
setter 內的 propagate 函式開始遍歷 flag 的依賴鏈表。

propagate 執行 link1 中儲存的 effect.run()

effect 函式重新執行,又讀取 flag.value。觸發了 getter。

此時問題出現了:執行完畢後,flag 的依賴鏈表結構如圖,現在有兩個節點 (link1, link2),但它們都指向同一個 effect,他們分別儲存 activeSub

執行結束後的鏈表:

day10-03

點擊第二次按鈕

day10-04

當按鈕又被點擊,flag.valuefalse 變為 true,再次觸發 setter。

propagate 開始遍歷依賴鏈表。但這一次,鏈表上有兩個節點 (link1 和 link2)。

propagate 先執行 link1 中的 effect.run()。effect 內部讀取 flag.value,再次觸發依賴收集,建立了一個新的 link3 節點加到鏈表尾部。

propagate 接著執行 link2 中的 effect.run()。effect 內部又一次讀取 flag.value,觸發依賴收集,又建立了一個新的 link4 節點並加到鏈表尾部。

執行結束後的鏈表:

day10-05

執行完成的鏈表結構

我們可以發現在觸發更新的時候,鏈表上的所有節點全部都會分別建立一個新的節點,因此發生了指數觸發 effect 的情況。

關鍵問題點

每次 Effect 重新執行時:

  1. 沒有檢查是否已經建立過 Link
  2. 盲目創建新的 Link 節點
  3. 導致鏈表無限增長

因此導致每次點擊按鈕,鏈表上的每一個 Link 都會建立新的 link,並且重複執行,造成指數級增長現象。

因為下個篇幅比較長就先講到這,大家要先理解問題的癥結點在哪,明天實作解決方案,才會知道為什麼要這樣做。


同步更新《嘿,日安!》技術部落格


上一篇
Day 9 - Effect:調度器實作應用
系列文
從零到一打造 Vue3 響應式系統10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言