前言
前端开发时时常碰到这样的业务,表单中某个值发生修改后调用如发送请求、显示对话框的方法。除了使用组件的回调事件外,我们也可以使用Vue提供的监听器Api来实现这样的需求。
概念
监听器 watch 是 Vue 内部提供的一个用于侦听功能的更通用的方法,其用来响应数据的变化,通过特定的数据变化驱动一些操作。Vue 官方文档解释当需要在数据变化时执行异步或开销较大的操作时,推荐使用该方法。
基本使用方法
在 Vue2 中,watch 是一个用于观察和响应 Vue 实例上的数据更改的选项。watch 选项允许你指定一个或多个观察者,当被观察的数据发生变化时,会触发相应的处理函数。
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
watch: {
message: function(newValue, oldValue) {
console.log('message changed from', oldValue, 'to', newValue);
}
}
});
在 Vue 3 中,watch 的使用方式与 Vue 2 类似,但有一些差异和改进。Vue 3 引入了 Composition API,使得组织和复用代码更加灵活。以下是 Vue 3 中使用 watch 的示例:
import { watch, ref } from 'vue';
export default {
setup() {
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
});
return { count };
}
};
深度监听
深度监听(deep watching)在 Vue 中是指不仅监听对象或数组本身的变化,还监听其内部嵌套属性或元素的变化。
例如,如果你有一个包含对象的数组,并且你想要监听数组中每个对象的属性变化,那么你就需要使用深度监听。Vue 会递归地观察这些对象或数组,以确保任何内部的变化都能被捕捉到。
Vue2 开启深度监听
new Vue({
el: '#app',
data: {
obj: {
name: 'Alice',
age: 25
}
},
watch: {
obj: {
handler: function(newValue, oldValue) {
console.log('obj changed:', newValue);
},
deep: true, // 深度观察对象内部的更改
immediate: true // 立即执行一次处理函数
}
}
});
Vue3 开启深度监听
import { watch, reactive } from 'vue';
export default {
setup() {
const state = reactive({ count: 0 });
watch(
() => state.count,
(newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`);
},
{ deep: true }
);
// 注意:在这个特定例子中,使用 deep: true 实际上是不必要的,因为我们只观察 state.count。
// 但如果你要观察整个 state 对象或其包含嵌套属性的部分,那么 deep: true 将是有用的。
return { state };
}
};
注意:深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。
watchEffect()
侦听器的回调使用与源完全相同的响应式状态是很常见的。例如下面的代码,在每当 toolId 的引用发生变化时使用侦听器来加载一个远程资源:
const todoId = ref(1)
const data = ref(null)
watch(
todoId,
async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
},
{ immediate: true }
)
特别是注意侦听器是如何两次使用 toolId 的,一次是作为源,另一次是在回调中。我们可以用 watchEffect() 来简化上面的代码。watchEffect() 允许我们自动跟踪回调的响应式依赖。上面的侦听器可以重写为:
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})
对于这种只有一个依赖项的例子来说,watchEffect() 的好处相对较小。但是对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。此外,如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性。