在 Vue 的响应式系统中,watch侦听器是处理数据变化的核心工具之一。它能够监听 Vue 实例(或组件)中数据的变动,并在数据发生变化时执行自定义的逻辑操作。无论是简单的数值变化监听,还是复杂的对象、数组深度监听,亦或是需要初始化时立即执行的场景,watch都能通过灵活的配置满足需求。本文将从基础用法到高级场景,全面解析watch侦听器的使用方式。
一、watch 侦听器的基础用法
watch的基础作用是监听单个数据的变化,当被监听的数据发生改变时,触发对应的处理函数。它的使用方式主要分为字符串形式(Vue2 选项式 API)、函数形式(Vue2 选项式 API)和组合式 API(Vue3) 两种场景,我们先从最基础的选项式 API 开始讲解。
1. 监听基本类型数据(Vue2 选项式 API)
对于String、Number、Boolean等基本类型数据,watch的基础用法非常直观。我们只需要在 Vue 实例的watch选项中,以数据名为键,以处理函数为值即可。
new Vue({
el: '#app',
data: {
message: 'Hello Vue',
count: 0
},
watch: {
// 监听message的变化
message(newVal, oldVal) {
console.log('message从', oldVal, '变为', newVal);
},
// 也可以写成对象形式,handler为处理函数(基础场景下和上面的写法等价)
count: {
handler(newVal, oldVal) {
console.log('count从', oldVal, '变为', newVal);
}
}
},
methods: {
changeMessage() {
this.message = 'Hello Watch';
},
increment() {
this.count++;
}
}
});
在上述代码中,当message或count的值发生变化时,对应的监听函数会被触发,函数的两个参数分别是新值(newVal)和旧值(oldVal)。
2. 组合式 API 中的基础监听(Vue3)
在 Vue3 的<script setup>语法中,我们使用watch函数来实现监听功能,它接收三个参数:监听源、处理函数和配置项。
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">修改消息</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
// 定义响应式数据
const message = ref('Hello Vue3');
// 监听ref数据
watch(message, (newVal, oldVal) => {
console.log('message从', oldVal, '变为', newVal);
});
const changeMessage = () => {
message.value = 'Hello Watch3';
};
</script>
对于reactive定义的对象属性,还可以通过函数返回值的形式指定监听源:
import { reactive, watch } from 'vue';
const user = reactive({
name: '张三',
age: 18
});
// 监听user.name的变化
watch(() => user.name, (newVal, oldVal) => {
console.log('用户名从', oldVal, '变为', newVal);
});
3. 监听多个数据
如果需要同时监听多个数据的变化,可以将监听源设置为一个数组,处理函数的参数也会变成对应的数据新值和旧值数组:
// Vue3组合式API
const count = ref(0);
const name = ref('张三');
watch([count, () => user.age], ([newCount, newAge], [oldCount, oldAge]) => {
console.log('count或age发生变化', newCount, newAge);
});
// Vue2选项式API
watch: {
'count'(newVal, oldVal) { /* ... */ },
'user.age'(newVal, oldVal) { /* ... */ },
// 也可以通过数组监听多个数据(需要借助$watch)
}
二、深度监听:处理对象 / 数组的嵌套变化
默认情况下,watch是浅监听的:它只会监听数据的引用变化,而不会监听对象内部属性或数组元素的变化。例如,对于一个对象user: { name: '张三', age: 18 },如果我们直接修改user.name,默认的watch不会触发。这时就需要用到深度监听(deep: true)。
1. Vue2 选项式 API 中的深度监听
new Vue({
el: '#app',
data: {
user: {
name: '张三',
info: {
age: 18,
address: '北京'
}
},
list: [1, 2, 3]
},
watch: {
// 监听对象的深度变化
user: {
handler(newVal, oldVal) {
console.log('user对象发生变化', newVal);
},
deep: true // 开启深度监听
},
// 监听数组的变化(数组的push/pop等方法会触发默认监听,但嵌套数组需要深度监听)
list: {
handler(newVal, oldVal) {
console.log('list发生变化', newVal);
},
deep: true // 数组嵌套时需要开启
}
},
methods: {
changeUserName() {
this.user.name = '李四'; // 开启深度监听后,此操作会触发watch
},
changeUserAddress() {
this.user.info.address = '上海'; // 嵌套属性变化也会触发
},
pushToList() {
this.list.push(4); // 数组普通操作默认触发,嵌套数组需要深度监听
}
}
});
需要注意的是,数组的原生方法(如push、pop、splice等)会改变数组的引用,因此默认的watch就能监听;但如果是数组的嵌套元素变化(如list[0].name = 'xxx'),则需要开启deep: true。
2. Vue3 组合式 API 中的深度监听
<template>
<div>
<button @click="changeUserAge">修改年龄</button>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
const user = reactive({
name: '张三',
info: {
age: 18
}
});
// 监听整个user对象的深度变化
watch(user, (newVal) => {
console.log('user发生变化', newVal);
}, { deep: true }); // 配置项中开启deep
// 也可以只监听嵌套属性(无需深度监听,更高效)
watch(() => user.info.age, (newVal) => {
console.log('年龄变化', newVal);
});
const changeUserAge = () => {
user.info.age = 20;
};
</script>
性能优化建议:深度监听会遍历对象的所有嵌套属性,当对象结构复杂时,可能会有性能损耗。因此,如果我们只需要监听对象的某个嵌套属性,直接通过() => user.info.age的形式监听该属性,比监听整个对象并开启深度监听更高效。
三、立即执行:初始化时触发监听函数
默认情况下,watch的处理函数只会在数据发生变化时触发,初始化时不会执行。但在某些场景下,我们需要在页面加载时就执行一次监听函数(例如,根据初始数据请求接口、初始化状态等),这时就需要用到immediate: true配置。
1. 基本使用场景
// Vue2选项式API
new Vue({
el: '#app',
data: {
userId: 1
},
watch: {
userId: {
handler(newVal) {
// 根据userId请求用户数据
this.fetchUserInfo(newVal);
},
immediate: true // 初始化时立即执行
}
},
methods: {
async fetchUserInfo(id) {
const res = await fetch(`/api/user/${id}`);
const data = await res.json();
console.log('用户信息', data);
}
}
});
在上述代码中,userId的监听函数会在 Vue 实例初始化时立即执行一次,之后当userId变化时,也会再次执行。这完美解决了 “初始数据加载” 的需求。
2. Vue3 组合式 API 中的立即执行
<script setup>
import { ref, watch } from 'vue';
const userId = ref(1);
watch(userId, async (newVal) => {
const res = await fetch(`/api/user/${newVal}`);
const data = await res.json();
console.log('用户信息', data);
}, { immediate: true }); // 配置项中开启immediate
// 手动修改userId
const changeUserId = () => {
userId.value = 2;
};
</script>
3. 立即执行 + 深度监听的组合使用
在实际开发中,我们经常需要同时开启immediate和deep,例如监听一个复杂的查询参数对象,初始化时执行查询,参数变化时重新查询:
// Vue3组合式API
const queryParams = reactive({
keyword: '',
page: 1,
size: 10
});
watch(queryParams, async (newVal) => {
// 根据查询参数请求数据
const res = await fetch(`/api/list?${new URLSearchParams(newVal)}`);
const data = await res.json();
console.log('列表数据', data);
}, {
immediate: true, // 初始化执行
deep: true // 监听参数对象的嵌套变化
});
// 修改查询参数
const changeKeyword = () => {
queryParams.keyword = 'vue';
};
四、总结与注意事项
- 基础用法:
watch用于监听数据变化,接收新值和旧值参数,Vue2 中通过选项式 API 配置,Vue3 中通过watch函数实现,支持监听单个或多个数据。 - 深度监听:通过
deep: true开启,用于监听对象内部属性、数组嵌套元素的变化,注意避免对复杂对象滥用深度监听,优先监听具体属性以提升性能。 - 立即执行:通过
immediate: true开启,用于初始化时执行监听函数,常见于数据初始化、接口请求等场景。 - 注意事项:
- Vue3 中,
watch监听reactive对象时,旧值参数会和新值相同(因为引用未变),如果需要获取旧值,可监听对象的属性而非整个对象。 - 不再需要的
watch(如组件销毁时),Vue3 的watch函数会返回一个停止监听的函数,可手动调用以释放资源。 - 对于简单的逻辑,可优先使用
computed计算属性,watch更适合处理异步操作或复杂的副作用逻辑。
- Vue3 中,
watch侦听器作为 Vue 响应式系统的重要组成部分,掌握其基础用法和高级配置,能让我们更优雅地处理数据变化带来的业务逻辑,提升代码的可维护性和性能。

3862

被折叠的 条评论
为什么被折叠?



