三歲小朋友也能懂的 Vue watch 清理副作用 (clean up)
三歲小朋友也能懂的 Vue watch
清理副作用 (clean up)
1. watch
是什麼?
小朋友,你有沒有玩過「聽寫遊戲」呢?
當老師說一個字,你就要寫下來對不對?
在 Vue 裡,watch
就像是在「聽」一個變數的變化。
只要變數變了,它就會「做一些事情」,例如去網路上抓新的圖片。
2. 什麼是「過期的副作用」?
現在想像一下,你在畫畫,老師叫你畫「小貓」,你開始畫。
但畫到一半,老師又說:「換畫小狗!」
然後又說:「換畫小兔子!」
結果,你的畫紙上畫滿了不同動物,亂七八糟的,對吧?
在 Vue 也是這樣:
watch
監聽一個變數當變數變了,它就會去「網路抓資料」
但是如果變數一直變,就會同時發出很多請求,最後收到的結果可能是錯的!
這就是「過期的副作用」,因為早先請求的資料已經沒用了,但它還是回來影響畫面。
3. 怎麼解決?用 onInvalidate
現在,想像老師給你一個橡皮擦,每次說「換畫小狗」,你就先擦掉「小貓」。
這樣你的畫紙上就不會亂七八糟,只有最新的畫。
在 Vue 裡,我們用 onInvalidate
來做這件事:
jsCopyEditwatch(message, async (newVal, oldVal, onInvalidate) => {
let expired = false // 這是標記,表示「舊的畫要擦掉」
onInvalidate(() => expired = true) // 這裡當新資料來時,舊的就過期
let res = await new Promise(resolve => {
setTimeout(() => {
console.log('call api')
resolve('新的圖片網址')
}, 3000) // 模擬 API 請求
})
if (!expired) { // 只有「沒有過期」的時候才更新畫面
image.value = res
}
})
4. onInvalidate
怎麼運作?
當
message
改變時,watch
會執行一次函式如果
message
又變了,onInvalidate
會讓上次的請求「失效」這樣舊的結果不會影響新的結果!
5. AbortController
進階版本
除了用 expired
這個標記,我們還可以用 AbortController
來取消請求:
jsCopyEditwatch(message, async (newVal, oldVal, onInvalidate) => {
const controller = new AbortController()
const signal = controller.signal
onInvalidate(() => {
console.log('取消舊請求')
controller.abort() // 取消請求
})
try {
let response = await fetch('https://picsum.photos/300/300', { signal })
if (!response.ok) throw new Error('錯誤')
image.value = response.url
} catch (error) {
if (error.name === 'AbortError') {
console.log('請求被取消了!')
}
}
})
這樣,每次新的請求來時,舊的請求就會被「砍掉」,不會占用資源。
🎨 小結:
watch
就像在「聽變數」,變了就做事但如果變數變太快,會有「過期的副作用」
onInvalidate
就像橡皮擦,讓舊的請求不影響新的AbortController
甚至可以直接「砍掉」網路請求
這樣 Vue 就不會亂七八糟,畫面只會顯示「最新」的結果! 🎉
請也幫我解釋 onCleanup onWatcherCleanup 謝謝!
想像你在玩積木,你有一座「變色城堡」,當你按下不同的按鈕,城堡會變成不同的顏色。但有時候,你按得太快,還沒變色完成就又按下一個顏色,結果顏色亂跳,甚至可能變成奇怪的顏色組合。
Vue 的 watch
也會遇到類似的問題:
當我們監聽某個變數的變化(比如 message
),如果變化太快,但 API 回應比較慢,就可能發生「過期的結果蓋掉新結果」的問題,這叫做 競態問題(Race Condition)。
onInvalidate
(onCleanup 的舊名字)
這時候我們可以用 onInvalidate
來「取消」舊的請求。簡單來說,就是當你按下一個新顏色時,先把上一次的變色過程停止,確保最後的結果是最新的。
Vue 3.5 新增的 onWatcherCleanup
onCleanup
適用於一般的watch
,用來清理舊的副作用(像是 API 請求、計時器)。onWatcherCleanup
適合用在 封裝好的函式庫,當watch
被銷毀時會自動清理。
差異:
onCleanup
用在 每次數值變更時,清理舊的影響。onWatcherCleanup
則是當 整個監聽行為結束(例如元件被卸載)時,做一次性的清理。
類比:
onCleanup
→ 當你換顏色時,先擦掉舊的顏色onWatcherCleanup
→ 當你不玩這座城堡時,把變色機關全部拆掉
這樣就能確保你的變色城堡不會變得亂七八糟啦!🎨✨