watch
是一个用于观察和响应 Vue 组件中响应式数据变化的工具。当组件中的某个响应式数据发生变化时,watch
可以帮助你执行一些特定的操作或副作用(side effects),比如更新其他数据、执行异步请求、调用方法等。
watch的用法
1. 基本用法
你可以使用 watch
来监听单个数据属性的变化:
<template>
<input v-model="firstName" placeholder="Enter your first name">
</template>
<script setup>
import { ref, watch } from 'vue';
const firstName = ref('');
watch(firstName, (newVal, oldVal) => {
console.log(`First name changed from ${oldVal} to ${newVal}`);
// 在这里可以执行一些副作用,例如异步请求等
});
</script>
2. 监听多个数据源
你也可以同时监听多个响应式数据的变化:
<template>
<div>
<input v-model="firstName" placeholder="Enter your first name">
<input v-model="lastName" placeholder="Enter your last name">
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const firstName = ref('');
const lastName = ref('');
watch([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName]) => {
console.log(`First name changed from ${oldFirstName} to ${newFirstName}`);
console.log(`Last name changed from ${oldLastName} to ${newLastName}`);
});
</script>
3. 深度监听对象
如果你想监听对象的内部属性变化,可以使用 deep
选项:
<template>
<input v-model="user.name" placeholder="Enter your name">
</template>
<script setup>
import { ref, watch } from 'vue';
const user = ref({ name: '' });
watch(user, (newVal, oldVal) => {
console.log('User object changed:', newVal);
}, { deep: true }); // 开启深度监听
</script>
注意,深度监听可能会带来性能开销,因为它会递归地遍历对象的所有属性。因此,在可能的情况下,尽量避免使用深度监听,或者只监听对象中的特定属性。
4. 立即执行回调函数
默认情况下,watch
的回调函数在数据第一次变化时不会执行。如果你希望回调函数立即执行一次,可以设置 immediate
选项为 true
:
watch(firstName, (newVal, oldVal) => {
console.log(`First name is now: ${newVal}`);
}, { immediate: true }); // 立即执行回调函数
5. 停止监听
watch
函数返回一个停止监听的函数,你可以调用这个函数来停止监听:
const stopWatch = watch(firstName, (newVal) => {
console.log(`First name is now: ${newVal}`);
});
// 停止监听
stopWatch();
6. 监听组件的 props 或 $emit
事件
你也可以使用 watch
来监听组件的 props 或监听子组件通过 $emit
触发的事件:
<script setup>
import { watch } from 'vue';
const props = defineProps({
propToWatch: String
});
const emit = defineEmits(['updateValue']);
watch(() => props.propToWatch, (newVal) => {
console.log(`propToWatch changed to: ${newVal}`);
});
watch(() => emit, (newVal, oldVal) => {
// 当子组件触发 'updateValue' 事件时,这里的回调函数会被调用
console.log('updateValue event emitted');
}, { deep: true }); // 注意这里可能需要深度监听,取决于你的具体用例
</script>
watch的使用场景
1. 异步数据获取
当某个数据属性变化时,你可能需要发送异步请求获取新数据。例如,用户在一个搜索框中输入内容时,你可能想根据输入内容实时搜索并显示结果。使用 watch
可以监听搜索框的值,并在其变化时发起搜索请求。
<template>
<input v-model="searchQuery" placeholder="Search...">
<div v-if="searchResults">{{ searchResults }}</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { fetchSearchResults } from './api';
const searchQuery = ref('');
const searchResults = ref(null);
watch(searchQuery, async (newQuery) => {
const results = await fetchSearchResults(newQuery);
searchResults.value = results;
});
</script>
2. 复杂数据变化处理
当需要处理复杂数据结构的变化时,watch
可以帮助你执行相应的逻辑。例如,你可能有一个对象,其中包含了多个属性,你需要在这些属性变化时执行一些操作。
<template>
<div>
<!-- 渲染表单输入 -->
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const formData = ref({
name: '',
email: '',
address: {
street: '',
city: ''
}
});
watch(formData, (newFormData, oldFormData) => {
// 当 formData 对象的任何属性变化时,执行某些操作
// 例如,验证表单数据或准备提交
}, { deep: true }); // 深度监听对象的变化
</script>
3. 监听 props 或 $emit
事件
在组件中,你可能需要监听父组件传递的 props 的变化,或者在子组件触发 $emit
事件时执行某些操作。
<script setup>
import { defineProps, defineEmits, watch } from 'vue';
const props = defineProps({
propToWatch: String
});
const emit = defineEmits(['updateValue']);
watch(() => props.propToWatch, (newVal) => {
// 当 propToWatch 变化时,执行操作
});
watch(() => emit, (newVal, oldVal) => {
// 当子组件通过 $emit 触发事件时,执行操作
}, { deep: true }); // 注意深度监听可能需要根据具体场景使用
</script>
4. 监听计算属性或方法的结果
虽然不常见,但有时你可能想监听一个计算属性或方法返回的结果的变化。这通常是因为计算属性或方法依赖于其他响应式数据,并且你希望在结果变化时执行某些操作。
<script setup>
import { ref, computed, watch } from 'vue';
const basePrice = ref(100);
const discount = ref(0.1);
const finalPrice = computed(() => basePrice.value * (1 - discount.value));
watch(finalPrice, (newPrice) => {
// 当 finalPrice 计算结果变化时,执行操作
});
</script>
注意事项:
- 当使用深度监听(
deep: true
)时,请注意性能影响,因为深度监听会递归地遍历对象的所有属性。如果可能,尽量避免深度监听,或者仅监听你关心的特定属性。watch
应该在组件的setup
函数中使用,以确保它能够在响应式数据变化时正确执行。- 在某些情况下,如果可以使用
computed
属性来派生新值,那么可能不需要使用watch
。computed
属性会自动更新,并且它们的结果可以被直接用于模板中,而不需要在每次变化时执行额外的逻辑。
watch的函数原理
watch
的工作原理依赖于 Vue 3 的响应式系统。每个响应式引用(通过 ref
创建)或响应式对象(通过 reactive
创建)都有一个与之关联的依赖收集器。当我们在模板或计算属性中使用这些响应式数据时,Vue 会自动将这些依赖记录下来。当响应式数据变化时,Vue 会触发所有相关依赖的更新,这包括 watch
的回调函数。