watch 總共有三個參數
val
: 第一個為 被監聽的變數newVal
: 當監聽數值變化時,產生的新數值oldVal
: 當監聽數值變化時,產生的舊數值
watch(val, (newVal, oldVal) => {
console.log(`new: ${newVal} ; old: ${oldVal}`);
});
ref & reactive watch 監控差異
下方範例測試簡單的 ref watch 範例來執行:
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const num = ref(0);
watch(num, (n, o) => {
console.log(`new: ${n} ; old: ${o}`);
});
setInterval(() => {
num.value++;
}, 2000);
return {
num,
};
},
};
Vue.createApp(App).mount("#app");
而當今天如果要改為 reactive 的話,我們先用上方的邏輯來嘗試撰寫一次
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const num1 = reactive({ idx: 0 });
watch(num1, (n, o) => {
console.log(`new: ${n} ; old: ${o}`);
});
setInterval(() => {
num1.idx++;
}, 2000);
return {
num1,
};
},
};
Vue.createApp(App).mount("#app");
結果會發現沒有任何 console 產生,並且在 devtool 新增了一個 warning 警告提示
![warn 警告](i.imgur.com/HaL44jY.png "warn 警告")
:::tip warn 警告提示意思是指 我現在 watch 能監控的數值,它只能被 getter,也就是只能被讀取的數值,所以需要透過一個函示,去將 reactive 的 key 數值,return 出來讓數值可以去 setter,下方為更改過後的程式碼: :::
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const num1 = reactive({ idx: 0 });
watch(
() => num1.idx,
(n, o) => {
console.log(`new: ${n} ; old: ${o}`);
}
);
setInterval(() => {
num1.idx++;
}, 2000);
return {
num1,
};
},
};
Vue.createApp(App).mount("#app");
ref & reactive 物件監聽差異
當今天 ref & reactive 兩者都為物件型別,在 watch 時,會發生一些不同。
ref 物件型別
是無法直接進行深層監聽的,需要使用其他的方法來處理,
也就是當今天 ref 裡頭有個 index:0
的數值,如果我只有觀察 num
整個 ref 的話,是無法被成功監聽的
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const num = ref({ idx: 0 });
watch(num, (n, o) => {
console.log(`new: ${n} ; old: ${o}`);
});
setTimeout(() => {
num.value.idx++;
}, 2000);
return {
num,
};
},
};
Vue.createApp(App).mount("#app");
ref 其實是可以觀察物件型別的,只是不能監控 ref 整個物件的變動,如果僅需要觀察物件為一個數值的話,可以使用下方的寫法:
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const refObj = ref({ idx: 0 });
watch(
()=> refObj.value.idx,
(n, o) => {
console.log(`new: ${n} ; old: ${o}`);
});
setTimeout(() => {
refObj.value.idx++;
}, 2000);
return {
refObj,
};
},
};
Vue.createApp(App).mount("#app");
如果真的必須得監控整個 ref 物件時,可以新增 deep:true
的屬性設定,但千萬要記得盡可能避免此寫法,因為瀏覽器是需要耗費較多的效能,去持續一個一個監聽數值被改變過後的樣子。
reactive 物件型別
如果為物件型別,可以透過函示去將觀察的數值回傳出來即可,成功解決
const { ref, reactive, watch } = Vue;
const App = {
setup() {
const num = ref({ idx: 0 });
const num1 = reactive({ idx: 0 });
watch(num, (n, o) => {
console.log(`new: ${n} ; old: ${o}`);
});
watch(
() => num1.idx,
(n, o) => {
console.log(`new: ${n} ; old: ${o}`);
}
);
setTimeout(() => {
num.value.idx++;
num1.idx++;
}, 2000);
return {
num,
num1,
};
},
};
Vue.createApp(App).mount("#app");
watchEffect
跟 watch 方法一樣,是在監聽數值的變更,只是有一些不同的使用方式以及特性
首先,watchEffect
是不需要傳入任何所需要監控的數值,直接使用 callback 函示即可,並且在讀取或是執行需要觀察的數值,即可自動加入監聽該數值,例如下方這段基礎的起手式:
const num = ref(0);
watchEffect(() => {
console.log(num.value)
});
:::tip
watchEffect
與 watch
最大的差異為:
watch
當所監控數值有變化時,才會去觸發。watchEffect
則是畫面在 mounted 完成時,就會先執行一次。
:::
stop()
當今天如果有需要監聽事件,在變化到一個數值,停止監聽時,可以使用 stop()
,這個 stop
是可以自定義名稱的,但建議用比較直覺性的名稱來表示,例如我這裡使用的名稱為 stop
const num = ref(0)
const stop = watchEffect(() => {
if (num.value >= 4) {
stop();
}
});
setInterval(() => {
num.value++;
}, 1000);
:::warning
這邊也再次強調,雖然我們透過 devtool 可以發現在 console 只有到 4 就沒有再繼續 console 了,
但實際上 stop()
只是關閉了整個 watchEffect()
,範例中下方的 setInterval()
依然在持續進行的!
:::
結論
ref 以及 reactive 在 watch 的使用上會稍微的不一樣,所以要去看是否有無要 watch 數值,去選擇要使用 ref or reactive。