Vue.js 是一个流行的前端框架,以其简单易用和强大的双向绑定功能而闻名。在 Vue 3 中,watch 功能得到了进一步增强,成为开发者处理复杂状态逻辑和响应式编程的强大工具。本文将带你体验watch的魅力。
什么是 watch?
有的人会问,主播主播,什么是watch? watch 是 Vue 提供的一种监听器,用于监视特定的数据属性或计算属性的变化,并在这些属性发生变化时执行相应的回调函数。它允许你对应用程序的状态做出反应,从而实现动态更新视图、调用 API 或触发其他业务逻辑。
为什么需要 watch?
虽然 Vue 的模板语法提供了简洁的方式来表达视图层的逻辑,但在某些情况下,我们需要更细粒度地控制何时以及如何响应数据的变化。比如:
- 当用户输入表单数据时自动验证;
- 在路由改变后重新获取数据;
- 实现复杂的业务规则,如购物车总价计算等。
这些都是 watch 大显身手的地方!
如何使用watch?
watch(source, callback, options)
- source:可以是单个响应式数据源(如
ref或者reactive对象),也可以是一个返回响应式数据的 getter 函数,甚至可以是一个包含多个数据源的数组。 - callback:当被监听的数据发生变化时调用的函数。它接收两个参数:新的值 (
newVal) 和旧的值 (oldVal)。 - options(可选):这是一个对象,允许你配置监听器的行为。
1. ref定义的数据
- 监视
ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变。 - 监视
ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。- 因为 JavaScript 对象是可变的,这意味着可以改变对象内部属性的值而不重新分配整个对象。为了确保能够正确地追踪到对象内部属性的变化,我们需要配置 watch 使用深度监听。
import { ref, watch } from 'vue';
const count = ref(0);
// 监听基本类型的 ref
watch(count, (newVal, oldVal) => {
console.log(`count changed to ${newVal} from ${oldVal}`);
});
// 改变 count 的值将会触发监听器
count.value++;
const user = ref({
name: '张三',
age: 25,
});
// 深度监听对象类型的 ref
watch(user, (newUser, oldUser) => {
console.log('user 发生了变化');
}, { deep: true });
// 改变 user 内部属性的值将会触发监听器
user.value.age = 26;
注意点:
- 若修改的是
ref定义的对象中的属性,newValue和oldValue都是新值,因为它们是同一个对象。 - 若修改整个
ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。 - 启用
deep选项可能会带来一定的性能开销,因为它会导致对整个对象的递归遍历。
2. reactive定义的数据
监视reactive定义的【对象类型】数据,且默认开启了深度监视。
import { reactive, watch } from 'vue';
const state = reactive({
name: '张三',
age: 25,
address: {
city: '北京',
street: '朝阳区'
}
});
// 监听整个 reactive 对象,默认即为深度监听
watch(state, (newState, oldState) => {
console.log('state 发生了变化');
});
// 改变 state 内部属性的值将会触发监听器
state.age = 26;
3. 侦听一个 getter 函数
使用
watch来监听由ref或reactive创建的对象的某个特定属性时,不能直接传递对象的属性(例如user.age)
- 若该属性值不是【对象类型】,需要写成函数形式。
- 若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
import {reactive,watch} from 'vue'
let person = reactive({
name:'张三',
age:18,
car:{
c1:'奔驰',
c2:'宝马'
}
})
// 监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
/* watch(()=> person.name,(newValue,oldValue)=>{
console.log('person.name变化了',newValue,oldValue)
}) */
// 监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
watch(()=>person.car,(newValue,oldValue)=>{
console.log('person.car变化了',newValue,oldValue)
},{deep:true})
4. 监听以上类型的值组成的数组
还有没有东西可以作为侦听器的源,有的兄弟,有的,还可以监视上述的多个数据。
import { reactive, ref, watch } from 'vue';
const state = reactive({
name: '张三',
age: 25
});
const count = ref(0);
// 组合监听多个属性或数据源
watch([() => state.name, () => state.age, count], ([newName, newAge, newCount], [oldName, oldAge, oldCount]) => {
console.log(`name or age or count changed. Name: ${newName}, Age: ${newAge}, Count: ${newCount}`);
});
// 改变任意监听的数据源都会触发监听器
state.age = 26;
count.value++;
支持立即执行
watch默认是懒加载的,但有时我们又希望在组件初始化时就执行监听器,为此,Vue 3 的 watch 提供了 immediate配置项。
watch(user, (newUser, oldUser) => {
console.log('user 发生了变化');
}, { immediate: true});
watchEffect:自动追踪依赖
除了传统的 watch,Vue 3 还引入了一个名为 watchEffect 的新方法。它会立即执行传入的函数,并且自动追踪其中使用的响应式依赖。每当这些依赖发生变化时,watchEffect 将重新运行该函数。
watchEffect(() => {
console.log(`当前 count 值为: ${count.value}`);
});
实战演练
让我们通过一个简单的例子来看看 watch 和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 name="Person">
import { ref, watch, watchEffect } from 'vue';
// 数据
let temp = ref(0);
let height = ref(0);
// 方法
function changeTemp() {
temp.value += 10;
}
function changeHeight() {
height.value += 1;
}
// 使用 watch 实现,明确指出要监视 temp、height
watch([temp, height], ([newTemp, newHeight]) => {
if (newTemp >= 50 || newHeight >= 20) {
console.log('联系服务器');
}
});
// 使用 watchEffect 实现,自动追踪依赖
const stopWatch = watchEffect(() => {
if (temp.value >= 50 || height.value >= 20) {
console.log('联系服务器');
}
if (temp.value === 100 || height.value === 50) {
console.log('清理了');
stopWatch();
}
});
</script>
在这个例子中:
watch提供了一种明确的方式去监听特定的响应式数据,并根据它们的变化执行相应的逻辑。watchEffect则提供了一种更加自动化的方式来处理响应式数据的变化,减少了手动管理依赖的需求,并且能够立即执行。
watch vs watchEffect 比较
1. 依赖声明方式
-
watch:- 显式地指定要监听的数据源。
- 可以监听单一的响应式数据(如
ref或reactive对象)或多个数据源(通过数组传递)。 - 适合当你明确知道哪些数据会影响你的业务逻辑时使用。
-
watchEffect:- 自动追踪回调函数中访问的所有响应式数据。
- 不需要显式列出依赖项,Vue 会自动识别并在相关数据变化时重新运行回调函数。
- 更加简洁,适合依赖关系不明显或动态变化的情况。
2. 初始执行时机
-
watch:- 默认情况下不会在定义时立即执行回调函数。
- 可以通过设置
{ immediate: true }来让watch在初始化时也触发一次回调。
-
watchEffect:- 总是在定义时立即执行一次回调函数,并且之后每次依赖的数据发生变化时都会重新执行。