深入了解 Vue watch 監聽器

Willy
Mar 19, 2021

--

immediate屬性

Vue 在最初綁定時是不會執行 watch的,要等到監聽的資料第一次改變後,才會開始執行監聽。如果要一開始就監聽資料就會用到immediate屬性。

deep屬性

若要監聽的資料是物件,我們要監聽這個物件裡面的資料時,就需要用到deep;若是沒使用deep,當day這個物件中的 year 或 month值被修改時,是不會觸發watch 的,只有day這個物件參考位址被改變時才會觸發。

watch特定物件的key

若今天你只需要監聽,物件中特定的key,可以使用字串的形式將你要監聽的object.key包起來就可以了。

watch連續觸發

在update這個methods中day被觸發了三次,但最終只會顯示最後一次觸發的值。

在vue中是使用Object.defineProperty去實現監聽的功能,當監聽的資料被設定時(this.day = 20210319),就會觸發Watcher,Watcher會關注你設定的資料,若被設定(改變)時,會呼叫自身的update方法,去執行watch中的方法(下圖就執行紅框處)。

而Watcher中的update方法是非同步的,每次會將watch中的方法,放入micro task 或macro task queue中,至於會放到哪一個,則是取決於目前的執行環境中是否有Promise、MutationObserver、setImmediate這些micro task api 有的會就會放入micro task queue,不管放入哪個queue都會等待同步任務處理完成才會執行queue中任務。所以在某時就會執行Watcher的update請求,也就是執行watch中的方法,在執行之前都不會再理會Watcher的update的請求。

在console中顯示

queueWatcher就是Watcher執行update的方法,主要是透過has[id]、flushing和waiting這三個flag去控制,最主要是使用has[id]去判斷是否連續執行。

而這三次執行每次傳的參數,都是同一Watcher實例,所以只有第一次has[id]為null,所以就會將has[id]設為true,後面兩次就不會再執行了。

所以即使連續觸發三次,但只有第一次會將Wathcer實例加入到queue中,但他會等待其他同步程式跑完後,才會執行(因為Watcher是非同步設計),最終執行時,資料會是最後修改的那一個,但其實只有第一次執行會觸發watch;而最後顯示的資料卻是20210319的原因。

最後要直到 flushSchedulerQueue執行過了,或是 resetSchedulerState 執行之後has[id]才會被重新設定。

sync屬性

最後如果你還是希望能夠連續觸發,那就在watch中寫sync:true就可以了,sync:true 時就會改為呼叫run方法,而不是預設的queueWatcher。

Watcher.prototype.run方法

watch 會在 hook後才被觸發

如果你在hook中改變watch監聽的data,當mounted跑完後才會觸發。

console 結果

而且就算改變data 3次也只會觸發一次,所以在hook中需觸發watch要小心使用。

console 結果

參考連結

參考連結2

--

--

Willy
Willy

Written by Willy

前端修練筆記本,記錄一些踩雷及學習過程,希望能順便幫助一些,在學習或開發路上卡關的人們。

No responses yet