在響應式狀態變化時執行特定操作,需要使用watch 監聽器
。這些操作可能包含副作用 (side effects)
,如發送請求、調用 API,或更新 DOM 以外的系統狀態,亦或是處理自定義的邏輯。
watch
可以監聽單一或多個響應式數據的變化
。與 computed 計算屬性不同,watch 的側重點不在於計算派生值,而在於觀察響應狀態變化後觸發的動作。
watch 可以綁定在數據
、計算屬性
、 getter 函式
上,偵測值變更前
和變更後
的變化並做出響應。
這邊針對字串純值輸入框、物件單一屬性輸入框及陣列索引值輸入框變更並觸發對應的監聽器為例子說明使用方式:
👉 Vue3 Options API Watch 基本使用方式實作連結
字串純值輸入框 Vue Template:
<input v-model="inputPureVal" id="inputVal" type="text">
javascript:
inputPureVal(newValue, oldValue) {
this.inputPureValMsg = `oldValue:${oldValue},newValue:${newValue}`;
}
物件單一屬性輸入框 Vue Template:
<input v-model="inputObjVal.name" id="inputObjVal" type="text">
javascript:
"inputObjVal.name": function (newValue, oldValue) {
this.inputObjValMsg = `oldValue:${oldValue},newValue:${newValue}`;
}
⭐ watch 支持使用.
的語法來監聽簡單的屬性路徑,但不支持使用像inputObjVal[name]
這樣的動態屬性名稱來監聽。
陣列索引值輸入框 Vue Template 搭配計算屬性:
<input v-model="inputArrVal[0]" id="inputArrVal" type="text">
<input v-model="inputArrVal[1]" id="inputArrVal" type="text">
計算屬性:
computed javascript:
computed: {
getCompositionItem() {
return `第一項的值:${this.inputArrVal[0]},第二項的值:${this.inputArrVal[1]}`;
}
}
watch javascript:
getFirstItem: function (newValue, oldValue) {
this.inputArrMsg = `oldValue:${oldValue},newValue:${newValue}`;
}
前面的範例可以知道,watch
監聽器可以監聽物件內特定屬性的變化,或通過計算屬性來監控特定陣列索引值的變動。這是因為watch
預設是淺層監控,也就是說,它只監控被觀察對象的第一層變化,因此直接監控整個物件無法得知其內部嵌套屬性的變動。
在這種情況下,可以使用watch
的option
設置來解決問題。首先,當需要深度監控物件時,可以使用deep
選項。若要使用這些選項,必須以物件形式來定義監聽器。這個物件應包括監控對象變動的 handler
函數,以及相關的option
設置。
透過巢狀的物件結構搭配watch
的deep option
案例觀看其變化:
👉 Vue3 Options API watch 搭配 deep option 實作連結
⭐ 當監控的對象不是純值而是一個物件時,因為物件的引用來源相同,即使觸發變更並修改了物件內的屬性,變化前後的值 (newValue 和 oldValue) 仍然指向同一個物件。因此,它們會相等。
Vue Template:
<input v-model="inputObjVal.likefruit[0]" id="inputObjVal" type="text">
Vue 數據巢狀結構:
data() {
return {
inputObjVal: {
name: "Antonio",
age: 18,
// 深層物件
likefruit: ["Apple", "Banana", "Orange"]
},
};
}
watch
預設是懶執行的,也就是當監控對象的狀態發生變化時才會觸發執行,而不會在初始化時立刻執行。這與computed
計算屬性不同,computed
在初始化時就會立即執行計算。如果需要watch
在監聽器初始化時立即執行一次,可以搭配immediate option
來實現。
透過查詢會員資料搭配 fakeapi 在初始化結束立即執行一次查詢的案例說明:
👉 Vue3 Options API watch 搭配 immediate option 實作連結
Vue Template:
<div id="app">
<label for="userid">用戶編號查詢(輸入完按 Enter):</label>
<input v-model.lazy="userid" type="text" id="userid" class="mb-4">
<div class="content">
<h2>API 返回資料:</h2>
<div v-show="!isLoading" class="text">{{ userInfo }}</div>
<div v-show="isLoading" class="text">資料讀取中。。。</div>
</div>
</div>
javascript:
watch: {
userid: {
async handler(newValue, oldValue) {
this.showLoading()
const data = await this.getUserinfo();
this.userInfo = data;
this.isLoading = false;
},
immediate: true
}
}
Vue 的響應式系統依賴於事件循環
。當數據發生變化時,Vue 不會立即同步地更新 DOM,而是會將所有變更操作合併到同一個事件循環中,等這個循環結束後才批次更新 DOM。這可以提高性能,避免多次重複更新導致不必要的開銷。
watch 監聽器的回調時機可以通過flush option
來調整監聽器的觸發時機:
比較flush:'pre'
及flush:'post'
兩者呈現的差別:
👉 Vue3 Options API watch option flush:"pre" vs flush:"post" 差異實作連結
流程說明:
stye height
的樣式style height
增加watch
監聽器,當style height
變化的時候,在回調函式內印出watch 變化前後
的差別,並且直接讀取content
DOM 元素高度進行比較。Vue Template:
<div :style="contentStyle" ref="content" class="content"></div>
<p>{{ usePostWatchMsg }}</p>
javascript:
watch: {
"contentStyle.height": {
handler(newValue, oldValue) {
this.usePostWatchMsg = `watch 舊高度:${oldValue} | watch新高度:${newValue} | 讀取 DOM 元素高度:${this.$refs.content.clientHeight}`;
},
flush: "post"
}
}
比較flush:'pre'
及flush:'sync'
兩者呈現的差別:
👉 Vue3 Options API watch option flush:"pre" vs flush:"sync" 差異實作連結
流程說明:
increment
並在數據改變前後印出 before 及 after 的訊息javascript:
const rootComponent = {
data() {
return {
count: 0,
orderMsg: []
};
},
methods: {
increment() {
this.orderMsg.push("methods increament before");
this.count += 1;
this.orderMsg.push("methods increament after");
}
},
watch: {
count: {
handler(newValue) {
this.orderMsg.push(`watch option pre newValue:${newValue}`);
},
flush: "sync"
}
}
}
在某些特定場景下,如果需要根據條件動態建立監聽器,可以使用 Vue 的this.$watch()
方法手動設定監聽器。此外,根據邏輯需求,可以主動銷毀這些動態建立的監聽器。
👉 Vue3 Options API 動態創建 watch 監聽器實作連結
流程說明
unwatch
數據當中this.unwatch()
銷毀創建的動態監聽器javascript:
startWatching() {
this.isWatching = true;
this.unwatch = this.$watch(
"inputVal",
(newValue, oldValue) => {
this.getAiMsg();
},
{ immediate: true }
);
},
// 銷毀動態創建的監聽器
stopWatching() {
if (this.unwatch) {
this.unwatch();
this.unwatch = null;
}
this.isWatching = false;
this.aiMsg = ""; // 重置 AI 消息
}
computed
根據響應式數據變化派生計算結果,watch
更側重於在數據變化時執行特定邏輯,如操作 DOM 元素、發送 API 請求等副作用。它可以觀察到數據變化前後的值(oldValue 和 newValue)
,並根據變化執行相應的操作。.
的方式串接路徑,精確的監聽單一屬性,避免不必要的效能消耗。watch
的監控選項(option)
時,需要以對象
的方式定義監聽器,並使用handler
作為回調函式來處理,才可以啟用相關選項:
watch
預設為懶執行
,只有在依賴的響應式數據發生變化時才會觸發。若需要在初始化
時立即執行,可以使用immediate
選項。watch
回調的觸發時機,包括pre
(預設,數據變化後但 DOM 更新之前)、post
(DOM 更新完成後)、和sync
(同步立即執行)。