Vue 的 watch & watchEffect 差別

watchwatchEffect 的差異

功能 / 特性 watch watchEffect
參數 (newValue, oldValue) ()
查詢舊值 可以 不能
觸發條件 屬性改變時觸發 (可設定 immediate 可使初始化時觸發一次) 初始化時觸發一次,隨後屬性改變時再觸發
屬性指定 需要指定監聽的屬性 不需要指定具體屬性
停止監聽 無法停止 可以主動停止監聽
多次觸發執行 是(可進行節流)

使用方式

基本用法

watch 在初始化時不會立即執行,必須等到監聽的值發生變化後才會觸發回調函數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup>
import { ref, watch, watchEffect } from 'vue'

const text = ref('你好')

watchEffect(() => {
console.log('watchEffect:', text.value)
})

watch(text, (newValue, oldValue) => {
console.log('watch text:', `新值: ${newValue}`, `舊值: ${oldValue}`)
})

watch(() => text.value, (newValue, oldValue) => {
console.log('watch text.value:', `新值: ${newValue}`, `舊值: ${oldValue}`)
})

// 兩秒後觸發值變更
setTimeout(() => {
text.value = 'Hello'
}, 2000)
</script>

示範圖1

使用 immediate 參數的 watch

當設定 immediate: true 時,watch 會在初始化時立即執行一次回調函數,並持續監聽該屬性的變化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<script setup>
import { ref, watch, watchEffect } from 'vue'

const text = ref('你好')

watchEffect(() => {
console.log('watchEffect:', text.value)
})

// 使用 immediate,使監聽在初次渲染後立刻觸發
watch(
text,
(newValue, oldValue) => {
console.log('watch1:', newValue, oldValue)
},
{ immediate: true }
)

watch(
() => text.value,
(newValue, oldValue) => {
console.log('watch2:', newValue, oldValue)
},
{ immediate: true }
)

// 兩秒後觸發值變更
setTimeout(() => {
text.value = 'Hello'
}, 2000)
</script>

示範圖2

停止監聽 watchEffect

watchEffect 返回一個停止監聽的函數,你可以在需要時調用它來停止監聽。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script setup>
import { ref, watch, watchEffect } from 'vue'

const count = ref(0)
const stop = watchEffect(() => {
console.log(`watchEffect: ${count.value}`)
})

const changeCount = () => {
count.value += 1

// 當 count 大於 5 時停止監聽
if (count.value > 5) stop()
}

watch(count, (newValue, oldValue) => {
console.log(`newValue: ${newValue}, oldValue: ${oldValue}`)
})
</script>

<template>
<div class="text-right">
<button type="button" @click="changeCount">
改變-count: {{ count }}
</button>
</div>
</template>

count 的值大於 5 時,watchEffect 會停止監聽。

示範圖3

物件監聽的用法

如果監聽的對象不是 Proxy 物件,必須使用 () => 屬性 的語法,否則會出現以下警告:

1
[Vue warn]: Invalid watch source:   A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.

以下範例展示了如何正確監聽整個物件或單一屬性。

整筆資料進行監聽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script setup>
import { ref, reactive, watch } from 'vue';

const product = ref({
name: '麥香奶茶',
price: 10,
num: 24,
})

watch(product.value, (n) => {
console.log('product:', n);
});

const user = reactive({
name: 'Mike',
age: 12,
email: '[email protected]',
})

watch(user, (n) => {
console.log('user:', n);
});

setTimeout(() => {
user.email = '[email protected]';
product.value.num += 24
}, 2000);
</script>

<template>
<div>
{{ user.email }}
<hr>
{{ product.num }}
</div>
</template>

watch object

單一資料進行監聽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script setup>
import { ref, reactive, watch } from 'vue';

const product = ref({
name: '麥香奶茶',
price: 10,
num: 24,
})

watch(() => product.value.num, (n) => {
console.log('product:', n);
});

const user = reactive({
name: 'Mike',
age: 12,
email: '[email protected]',
})

watch(() => user.email, (n) => {
console.log('user:', n);
});

setTimeout(() => {
user.email = '[email protected]';
product.value.num += 24
}, 2000);
</script>

<template>
<div>
{{ user.email }}
<hr>
{{ product.num }}
</div>
</template>

watch object success

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script setup>
import { ref, reactive, watch } from 'vue';

const product = ref({
name: '麥香奶茶',
price: 10,
num: 24,
})

watch(product.value.num, (n) => {
console.log('product:', n);
});

const user = reactive({
name: 'Mike',
age: 12,
email: '[email protected]',
})

watch(user.email, (n) => {
console.log('user:', n);
});

setTimeout(() => {
user.email = '[email protected]';
product.value.num += 24
}, 2000);
</script>

<template>
<div>
{{ user.email }}
<hr>
{{ product.num }}
</div>
</template>

watch object error

總結

  • watch 主要用於監聽特定資料的變化,並允許在回調中訪問舊值。使用時需要明確指定要監聽的屬性,並且可以選擇是否在初始化時立即執行一次回調。這使得 watch 非常適合用來處理具體資料變動的場景,尤其是當你需要在資料變化時執行某些操作或獲取舊值時。

  • watchEffect 則是用來自動追蹤並處理資料變化後的後續自動識別在回調中使用到的所有響應式資料,操作。它會並且不需要你明確指定要監聽的屬性。這使得 watchEffect 特別適用於需要處理多個資料變化並且不確定具體哪些資料會變動的情境。watchEffect 也可以返回一個停止監聽的函數,允許手動控制監聽的結束。

總體來說,watchwatchEffect 各有不同的使用場景。當需要精確控制監聽某一屬性並且希望訪問舊值時,watch 是理想選擇;而當需要自動追蹤多個資料變化並執行後續操作時,watchEffect 更為便捷。選擇何者取決於你的具體需求,兩者在 Vue 中各自扮演著不同但互補的角色。