watch() 和 watchEffec()
# watch() 和 watchEffec()
# 一、watch的使用
# 1. watch
的作用
watch
是 Vue 3 中用于监听数据变化的工具。与 Vue 2 的 watch
功能一致,watch
允许我们监控数据变化并执行对应的逻辑。
# 2. 特点
- Vue 3 中的
watch
可以监听以下四种数据:- 使用
ref
定义的数据。 - 使用
reactive
定义的数据。 - 函数返回的值(
getter
函数)。 - 包含上述内容的数组。
- 使用
- 当被监听的数据发生变化时,Vue 会自动调用回调函数,
newValue
和oldValue
作为参数传递到回调函数中。
# 3. 语法格式
watch(source, callback, options?)
1
source
:被监听的数据,可以是ref
、reactive
、getter
函数或包含上述内容的数组。callback
:回调函数,数据发生变化时执行,接收两个参数:newValue
和oldValue
。options
:可选参数对象:deep
:是否开启深度监听,默认为false
。immediate
:是否在初始挂载时立即执行回调,默认为false
。
**即时回调** (`immediate: true`)
- 当你用
watch
监听数据时,默认情况下,只有当监听的数据发生变化时,回调函数才会被触发。 - 如果加上
immediate: true
,watch
会 在刚开始时就立刻执行一次,即使数据没有变化。
# 4. 常见使用场景
# 4.1 监听 ref
的基本类型数据
监听 ref
定义的基本类型数据:
let count = ref(0);
watch(count, (newValue, oldValue) => {
console.log('count 变化了', newValue, oldValue);
});
1
2
3
4
5
2
3
4
5
# 4.2 监听 ref
的对象类型数据
监听 ref
定义的对象类型数据时:
- 默认监听的是对象的地址值变化。
- 需要手动开启
deep: true
来监听对象内部属性变化。
let person = ref({ name: '张三', age: 18 });
watch(person, (newValue, oldValue) => {
console.log('person 变化了', newValue, oldValue);
}, { deep: true });
1
2
3
4
5
2
3
4
5
# 4.3 监听 reactive
的对象类型数据
监听 reactive
定义的对象时:
- 默认开启深度监听。
reactive
对象的地址值不会改变,因此监听到的newValue
和oldValue
通常是相同的对象。
let person = reactive({ name: '张三', age: 18 });
watch(person, (newValue, oldValue) => {
console.log('person 变化了', newValue, oldValue);
});
1
2
3
4
5
2
3
4
5
# 4.4 监听特定属性
监听 reactive
或 ref
对象中的特定属性:
// 监听基本类型属性
watch(() => person.name, (newValue, oldValue) => {
console.log('person.name 变化了', newValue, oldValue);
});
// 监听对象类型属性
watch(() => person.car, (newValue, oldValue) => {
console.log('person.car 变化了', newValue, oldValue);
}, { deep: true });
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4.5 监听多个数据
可以通过数组监听多个数据:
watch([() => person.name, person.age], (newValues, oldValues) => {
console.log('person.name 或 person.age 变化了', newValues, oldValues);
});
1
2
3
2
3
# 4.6 监听 getter
函数
watch
可以监听一个 getter
函数返回的值。当该函数的返回值变化时,会触发回调函数:
let person = reactive({ name: '张三', age: 18 });
watch(() => person.age > 18, (newValue, oldValue) => {
console.log('是否成年发生了变化', newValue, oldValue);
});
1
2
3
4
5
2
3
4
5
# 4.7 监听回调函数的返回值
# 5. 特殊场景使用
# 5.1 监听路由变化
监听 Vue Router 的路由变化:
import { useRoute } from 'vue-router';
const route = useRoute();
watch(() => route.path, (newPath, oldPath) => {
console.log('路由变化了', newPath, oldPath);
});
1
2
3
4
5
6
7
2
3
4
5
6
7
# 5.2 监听 props
数据
监听子组件的 props
数据:
props: {
user: {
type: Object,
required: true
}
},
setup(props) {
watch(() => props.user, (newValue, oldValue) => {
console.log('props.user 变化了', newValue, oldValue);
}, { deep: true });
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 5.3 监听 Vuex 的数据
通过 watch
监听 Vuex 状态的变化:
import { useStore } from 'vuex';
const store = useStore();
watch(() => store.state.count, (newValue, oldValue) => {
console.log('Vuex state.count 变化了', newValue, oldValue);
});
1
2
3
4
5
6
7
2
3
4
5
6
7
# 5.4 监听异步数据加载完成
在异步数据加载时使用 watch
监听其状态:
let dataLoaded = ref(false);
watch(dataLoaded, (newValue) => {
if (newValue) {
console.log('数据加载完成');
}
});
// 模拟数据加载
setTimeout(() => {
dataLoaded.value = true;
}, 2000);
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 6. 使用注意事项
- 深度监听:当需要监听对象内部属性时,需手动开启
deep: true
。 - 即时回调:当需要在初始阶段触发回调时,使用
immediate: true
。 - 性能优化:避免对大型数据对象开启深度监听,可能影响性能。
- 停止监听:可以通过调用
watch
返回的停止函数来手动停止监听。
const stopWatch = watch(count, (newValue, oldValue) => {
console.log('count 变化了', newValue, oldValue);
});
stopWatch(); // 停止监听
1
2
3
4
5
2
3
4
5
# 7. 使用小结
# 7.1 不涉及嵌套属性:可以直接监听
如果监听的是 ref
或 reactive
定义的顶层属性,可以直接传入 ref
或 reactive
数据作为 watch
的第一个参数,而无需使用回调函数。例如:
let count = ref(0);
// 直接监听
watch(count, (newValue, oldValue) => {
console.log('count 变化了', newValue, oldValue);
});
// 使用回调函数监听
watch(() => count.value, (newValue, oldValue) => {
console.log('count 变化了', newValue, oldValue);
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 7.2 涉及嵌套属性:必须使用回调函数
如果监听的是嵌套属性(无论是 ref
还是 reactive
定义的对象内部的属性),需要通过回调函数显式访问嵌套属性,以确保 Vue 捕获依赖关系。例如:
监听 reactive
对象的嵌套属性
let person = reactive({ name: '张三', details: { age: 18 } });
// 必须使用回调函数监听嵌套属性
watch(() => person.details.age, (newValue, oldValue) => {
console.log('person.details.age 变化了', newValue, oldValue);
});
1
2
3
4
5
6
2
3
4
5
6
监听 props
或 store
的嵌套属性
// props 数据
watch(() => props.user.name, (newValue, oldValue) => {
console.log('props.user.name 变化了', newValue, oldValue);
});
// Vuex 数据
watch(() => store.state.user.details.age, (newValue, oldValue) => {
console.log('store.state.user.details.age 变化了', newValue, oldValue);
});
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
总结
- 顶层属性: 直接监听即可,无需回调函数。
- 嵌套属性: 必须通过回调函数监听,以确保 Vue 能正确追踪属性的依赖。
- 推荐习惯: 对于复杂场景,使用回调函数监听属性可以避免遗漏依赖,是一种更安全的方式。
# 二、watchEffect的使用
作用:watchEffect
用于立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
watch
对比 watchEffect
- 都能监听响应式数据的变化,不同的是监听数据变化的方式不同。
watch
:需要明确指出监视的数据。watchEffect
:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
# 1. watchEffect
语法
import { ref, watchEffect } from 'vue';
// 定义响应式数据
let temp = ref(0);
let height = ref(0);
// 使用 watchEffect 监听数据变化
const stopWatch = watchEffect(() => {
// 函数内部用到的响应式数据 temp 和 height 被自动追踪
if (temp.value >= 50 || height.value >= 20) {
console.log('联系服务器');
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- effect: 一个函数,
watchEffect
会立即运行这个函数,并响应式地追踪其依赖。在依赖变化时,会重新执行该函数。
可选的 onCleanup
函数
在 watchEffect
函数内部,可以调用 onCleanup
注册清理回调,该回调在副作用重新运行前或停止跟踪时调用。
import { ref, watchEffect } from 'vue';
// 定义响应式数据
let temp = ref(0);
let height = ref(0);
// 使用 watchEffect 监听数据变化
const stopWatch = watchEffect((onCleanup) => {
// 函数内部用到的响应式数据 temp 和 height 被自动追踪
if (temp.value >= 50 || height.value >= 20) {
console.log('联系服务器');
}
// 注册一个清理回调,在副作用重新运行前或停止跟踪时调用
onCleanup(() => {
console.log('清理副作用');
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2. 使用 watchEffect
<template>
<div class="person">
<h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1>
<h2 id="demo">水温:{{ temp }}</h2>
<h2>水位:{{ height }}</h2>
<button @click="changeTemp">水温 +10</button>
<button @click="changeHeight">水位 +1</button>
</div>
</template>
<script lang="ts" setup>
import { ref, watchEffect } from 'vue';
// 定义响应式数据
let temp = ref(0);
let height = ref(0);
// 方法:改变 temp 和 height 的值
function changeTemp() {
temp.value += 10;
}
function changeHeight() {
height.value += 1;
}
// 使用 watchEffect 监听数据变化,不用明确指出监视的数据
const stopWatch = watchEffect((onCleanup) => {
// 函数内部用到的响应式数据 temp 和 height 被自动追踪
if (temp.value >= 50 || height.value >= 20) {
console.log(document.getElementById('demo')?.innerText);
console.log('联系服务器');
}
// 注册一个清理回调,在副作用重新运行前或停止跟踪时调用
onCleanup(() => {
console.log('清理副作用');
});
// 如果水温达到 100℃,或水位达到 50cm,取消监视
if (temp.value === 100 || height.value === 50) {
console.log('清理了');
stopWatch(); // 停止监听
}
});
</script>
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
37
38
39
40
41
42
43
44
45
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
37
38
39
40
41
42
43
44
45
总结
watchEffect
用于立即运行一个函数,并响应式地追踪其依赖。在依赖更改时重新执行该函数。- 不需要明确指出要监视的数据,函数内部用到的响应式数据会被自动追踪。
- 可以注册清理回调
onCleanup
,在副作用重新运行前或停止跟踪时调用。
编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08