vue3 watch 的多种场景用法(ref,reactive,getter函数,数组)
上面是vue3官方文档给出的说明,看不懂没关系,接下来我将会针对实际中各种场景给出说明,当你全部看完后再回来看这个说明,就会一目了然。
监听单个数据
- ref基本数据类型
- ref响应式对象的单个数据
- reactive响应式对象的单个数据
ref基本数据类型
import { watch,ref} from 'vue' // 导入watch和ref
// 响应式变量
const sum = ref(1)
// 绑定函数
function changeSum(){
sum.value += 1
}
// 对sum进行监听
watch(sum, (newValue,oldValue) => {
console.log('sum改变了')
}, { immediate: true })
watch是一个
函数
,接收的参数有三个:
- 监听的数据
sum
,在ref定义的基本数据类型中,不需要加value- 回调函数
(newValue,oldValue) => { console.log('sum改变了') }
,回调函数接收两个参数,newValue新值,oldValue旧值- 配置对象
{ immediate: true })
,表示立即监听,即从声明const sum = ref(1)
时就会触发回调函数,此时newValue为1,oldValue为undefined
ref响应式对象的单个数据
import { watch, ref } from 'vue';
// 响应式变量
const person = ref({
name: '马嘉祺',
age: 18
});
// 监听 person.name 的变化
watch(
() => person.value.name, // 返回需要监听的响应式引用
(newValue, oldValue) => {
console.log(`name 从 ${oldValue} 变为了 ${newValue}`);
}
);
// 模拟 name 属性变化
function changeName(newName) {
person.value.name = newName;
}
对于监听ref响应式对象的单个属性,watch函数并不支持直接监听person.value.name,引起其本身不是ref定义的(ref定义的是person),所以我们需要写成一个getter函数的返回形式
() => person.value.name
reactive响应式对象的单个数据
import { reactive, watch } from 'vue';
// 创建一个响应式对象
const person = reactive({
name: '马嘉祺',
age: 18
});
// 监听 person 对象中的 name 属性
watch(
() => person.name, // 返回需要监听的响应式属性
(newValue, oldValue) => {
console.log(`name 从 ${oldValue} 变为了 ${newValue}`);
}
);
// 模拟 name 属性变化
function changeName(newName) {
person.name = newName;
}
在监听对象的单个属性时,ref和reactive的用法没有太大区别,可能也就是
.value
的区别
监听整个对象
- ref监听
import { ref, watch } from 'vue';
// 使用 ref 定义一个响应式对象
const person = ref({
name: '马嘉祺',
age: 18,
address: {
city: '北京',
country: '中国'
}
});
// 监听整个 person 对象的变化,也可以写成函数返回形式 () => person.value
watch(person, (newValue, oldValue) => {
console.log('person 对象发生了变化:', newValue);
// 注意:这里 newValue 和 oldValue 都是对象的引用
}, { deep: true }); // 深度监听,确保内部属性变化也能触发回调
// 模拟 person 对象的 name 属性变化
function changeName(newName) {
person.value.name = newName;
}
// 模拟 person 对象的 address 属性变化
function changeAddress(newCity, newCountry) {
person.value.address.city = newCity;
person.value.address.country = newCountry;
}
- 对于ref定义的响应式对象,既可以写
person
(本身就是ref定义的)也可以写成函数返回形式() => person.value
- 回调函数仍然是接收两个参数
newValue新值,oldValue旧值
,不过这里的值都将是整个person对象
,而不是其中的某一个改变的值- 配置对象中,
{ deep: true }
表示开启深层监听,即监听对象里面每一个键值对的改变,如果不加,则watch只会监听person整个对象的变化(对象是引用类型,实际person指向的是地址,只有改变地址把整个对象换掉,才会触发watch)
- reactive监听
import { reactive, watch } from 'vue';
// 创建一个响应式对象
const person = reactive({
name: '马嘉祺',
age: 18,
address: {
city: '北京',
country: '中国'
}
});
// 监听整个 person 对象的变化
watch(
person, // 也可以直接用函数返回形式() => person
(newValue, oldValue) => {
console.log('person 对象发生了变化:', newValue);
},
// { deep: true } reactive默认开启深层监听,不需要自己写
);
// 模拟 person 对象的 name 属性变化
function changeName(newName) {
person.name = newName;
}
// 模拟 person 对象的 address 属性变化
function changeAddress(newCity, newCountry) {
person.address.city = newCity;
person.address.country = newCountry;
}
- 对于ref定义的响应式对象,既可以写
person
(本身就是reactive定义的)也可以写成函数返回形式() => person
- 回调函数仍然是接收两个参数
newValue新值,oldValue旧值
,不过这里的值都将是整个person对象
,而不是其中的某一个改变的值- 配置对象中,
{ deep: true }
表示开启深层监听,但是在reactive定义的响应式对象中,是默认开启的
监听对象中的部分数据
前面已经讲过了ref定义,reactive定义,函数返回形式,在vue3的官方文档中还给出了数组形式的监听,就是用于监听对象中部分数据
场景:用户信息中有几十个属性,而实际只需要监听用户的余额和年龄,监听单个实现不了,监听全部频繁触发影响性能
- 可以写多个watch函数监听单个数据
- 用数组的形式写一次即可
import { watch,reactive } from 'vue';
const person = reactive({
name: '马嘉祺',
age: 18,
address: {
city: '北京',
country: '中国'
}
});
// 使用 watch 函数监听 person.name 和 person.address.city
watch(
// 传入一个数组,包含要监听的响应式源,单个数据推荐采用函数返回形式
[() => person.name, () => person.address.city],
// 回调函数接收两个参数:新值数组[newName, newCity]和旧值数组[oldName, oldCity]
([newName, newCity], [oldName, oldCity]) => {
console.log(`Name changed from ${oldName} to ${newName}`);
console.log(`City changed from ${oldCity} to ${newCity}`);
}
);
- 监听对象要写成数组形式
[() => person.name, () => person.address.city]
每个元素都是一个函数返回形式- 回调函数中仍然是两个参数,前者是新值后者是旧值,但是类型都变成了数组,不要误认为是
([newName, newCity], [oldName, oldCity])
,新的全在前面,旧的全在后面
停止监听
import { reactive, watch } from 'vue';
const person = reactive({
name: '马嘉祺',
age: 18,
address: {
city: '北京',
country: '中国'
}
});
// 设置一个监听器,并立即检查是否需要停止监听
const stopWatch = watch(
[() => person.name, () => person.address.city],
([newName, newCity], [oldName, oldCity]) => {
console.log(`Name changed from ${oldName} to ${newName}`);
console.log(`City changed from ${oldCity} to ${newCity}`);
if (newName == '') {
console.log('停止监听');
stopWatch (); // 停止监听
},
{ immediate: true } // 立即执行回调函数
);
watch是一个函数,他的返回值也是一个函数,也就是用来停止监听的函数,我们用
stopWatch
变量进行接收,然后在变化的时候(也可以写在watchEffect
里面,但是这篇文章只介绍watch
)调用stopWatch()
即可停止监听
总结:在实践中的用法
- watch的第一个参数响应式属性,不管是单个基本数据,还是整个对象,统一写成函数返回形式
() => person.age 或 () => person
,person无论是ref或者reactive定义的都可以适用,当要同时监听多个数据的时候,也是采用数组包函数返回形式。 - 回调函数的两个参数,前者永远是新值,后者永远是旧值,根据监听的数据是单个还是多个决定参数是值
(newName,oldName) => {具体逻辑}
还是数组([newName, newCity], [oldName, oldCity]) => {具体逻辑}
- 配置对象,主要有两个
{deep:true}
用于开启深层监听(主要用于ref定义的响应式对象,可以监听到对象的每一个属性;{immediate:true}
用于开启立即监听,在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。 - 可以用一个变量接收监听器watch函数(返回值是一个停止函数),在某下情况下调用变量停止监听