小心遺漏的 watch 讓網頁爆炸
最近注意到 watch 有一個有趣的現象,一個可能會挖坑的現象。
在某些特殊情境下,watch 的位置可能不會在元件最外層。
類似這種感覺:
<script setup>
import { ref, watch } from 'vue'
const data = ref(1);
setTimeout(() => {
watch(data, (value) => {
console.log('[ Comp ] data: ', value);
})
}, 500);
</script>
先別吐槽這樣寫超廢,只是個簡單的示意啦。(ʘ ͜ʖ ʘ)
其實如果有這類需求,推薦使用 VueUse 提供的 watchPausable、watchTriggerable 這類進階版 watch,看起來更漂亮、更好維護。
我們知道正常情況下,元件 onUnmounted 後,watch 就會自動解除。
但是剛剛的那個例子,onUnmounted 後 watch 不會自動解除喲!ლ(╹◡╹ლ)
所以如果頁面中有很多這類 watch,很有可能讓網頁爆掉唷!o(≧∀≦)o
路人:「是在開心個毛線喔。(゚Д゚*)ノ」
這是因為如果在內層呼叫 watch,會讓 watch 與元件處在不同 effectScope,導致此結果。
該怎麼辦?(っ °Д °;)っ
解法也很簡單,勤奮一點手動解除。◝( •ω• )◟
<script setup>
import { ref, watch } from 'vue'
const data = ref(1);
let unwatch;
setTimeout(() => {
unwatch = watch(data, (value) => {
console.log('[ CompUnwatch ] data: ', value);
})
}, 500);
onUnmounted(() => {
console.log('[ onUnmounted ]');
unwatch();
})
</script>
路人:「啊如果有很多 watch,這樣一個一個解除很累欸。Σ(ˊДˋ;)」
鱈魚:「…('◉◞⊖◟◉` ),先不探究這種 code 怎麼過得了 code review ,假設真的有很多 watch 要解除,可以用剛剛提到的關鍵字『effectScope』」
effectScope 可以用來捕捉範圍內的 reactive effects,所以可以把所有的 watch 收集起來統一解除。
例如像這樣:
<script setup>
import { ref, watch, onUnmounted, effectScope } from 'vue'
const scope = effectScope()
const data = ref(1);
const number = ref(0);
setTimeout(() => {
scope.run(() => {
watch(data, (value) => {
console.log('[ CompScope ] data: ', value);
})
})
}, 500);
setTimeout(() => {
scope.run(() => {
watch(number, (value) => {
console.log('[ CompScope ] number: ', value);
})
})
}, 1000);
onUnmounted(() => {
console.log('[ onUnmounted ]');
scope.stop();
})
</script>
世界再度回復和平!✧⁑。٩(ˊᗜˋ*)و✧⁕。
以上提到的範例可以在此連結查看。
可以觀察到即使元件消失後,devtool 的 console 依舊持續跑出 log!(゚Д゚*)ノ
不過說的真,請不要這樣用 watch。…(›´ω`‹ )
總結 🐟
- watch 不會自動解除的原因是在不同 effectScope
- 使用 effectScope 可以統一解除 watch