vue3侦听器
前言
侦听器的作用不同与计算属性,侦听器的作用是当数据变更的时候执行一系列的“副作用”。
watch中接收三个参数,watch(source, callback, options)
-
source: 需要监听的响应式数据或者函数
-
callback:监听的数据发生变化时,会触发 callback
- newValue:数据的新值
- oldValue:数据的旧值
- onCleanup:函数类型,接受一个回调函数。每次更新时,会调用上一次注册的 onCleanup 函数
-
options:额外的配置项
- immediate:Boolean类型,是否在第一次就触发 watch
- deep:Boolean 类型,是否开启深度监听
- flush:pre | post | sync
- pre:在组件更新前执行副作用
- post:在组件更新后运行副作用
- sync:每个更改都强制触发 watch
- onTrack:函数,具备 event 参数,调试用。将在响应式 property 或 ref 作为依赖项被追踪时被调用
- onTrigger:函数,具备 event 参数,调试用。将在依赖项变更导致副作用被触发时被调用。
简单数据类型的监听
let x = ref(1)
let y = ref(2)
// 监听单个ref
watch(x, (newVal, oldVal) => {
console.log(newVal);
console.log(oldVal);
})
// 监听getter函数
watch(() => x.value + y.value, (newVal, oldVal) => {
console.log(newVal);
console.log(oldVal);
})
//监听多个数据源组成的数组
watch([x, y], ([newX, newY]) => {
console.log(newX);
console.log(newY);
})
setTimeout(() => {
x.value = 100
y.value = 200
}, 2000)
值得注意的是这里的watch和vue2中的watch有所不同,vue2中的watch单个组件中只写一个,把所有的监听对象都放在一个watch中进行监听,vue3中可以写多个watch,每一个watch可以监听一条数据或者多条数据。
深层侦听器
监听整个对象
let data = reactive({
person: {
name: '张三',
age: 18,
hobby: {
ball: '打篮球'
}
}
})
watch(data, (newValue, oldValue) => {
console.log(newValue === oldValue); //true
console.log(newValue, oldValue);
})
setTimeout(() => {
data.person.age = 100
data.person.hobby.ball = '打乒乓球'
}, 2000);
当watch监听的是整个对象的时候,无论是对于对象的第一层属性进行修改还是嵌套的多层属性进行修改,都会被watch监听到,同时对于监听函数返回的 newValue 和 oldValue 的值是相等的,都是最新的 data。
监听对象的某个属性
let data = reactive({
person: {
name: '张三',
age: 18,
hobby: {
ball: '打篮球'
}
}
})
watch(
() => data.person.age,
(newValue, oldValue) => {
console.log(newValue === oldValue); //false
console.log(newValue, oldValue); // 100 ,18
})
setTimeout(() => {
data.person.age = 100
data.person.hobby.ball = '打乒乓球'
}, 2000);
当监听的是对象中某个属性的时候,需要使用函数的写法,而不可以直接监听属性值,例如:
watch(
data.person.age, //这里的data.person.age会直接返回number,并不能被监听
(newValue, oldValue) => {
console.log(newValue === oldValue); //false
console.log(newValue, oldValue); // 100 ,18
})
当监听的是对象中属性值的时候,会正确的返回 newValue 和 oldValue。
监听对象的第一层属性
let data = reactive({
person: {
name: '张三',
age: 18,
hobby: {
ball: '打篮球'
}
}
})
watch(
() => ({ ...data.person }),
(newValue, oldValue) => {
console.log(newValue, oldValue); //不会执行
})
setTimeout(() => {
data.person.hobby.ball = '打乒乓球'
}, 2000);
使用扩展运算符是对data进行浅拷贝,当改变 data.person.hobby.ball 的时候不会触发 watch ,这是由于这里只会对于第一层数据进行监听。假如改变 age 则会触发watch。
watch(
() => ({ ...data.person }),
(newValue, oldValue) => {
console.log(newValue, oldValue);
})
setTimeout(() => {
data.person.age = 100
data.person.hobby.ball = '打乒乓球'
}, 2000);
这个时候由于改变 ball 的同时改变了 age ,这个时候会触发 watch ,同时对于 age 和 ball 的修改后的新的数据都会返回。
watch总结
- vue2中watch使用的选项式 api 的形式,一个组件中只能配置一个watch对象
- vue3中watch使用的组合式 api的形式,一个组件中可以配置多个watch
- vue2中的watch 对于对象的监听需要使用 handler 函数的形式,同时需要设置 deep: true 属性,确保可以深度监听。
- vue3中的watch对于对象的监听,可以直接监听,同时默认设置的是 deep:true
- vue3中的watch对于对象的监听,返回的 newValue 和 oldValue是相等的
watchEffect
watch vs. watchEffect 官方介绍
watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
大概的意思
- 与watch相比,watchEffect在使用时更加抽象,不需要明确指定依赖是谁,直接使用就可以了。
- watchEffect是非惰性的,一旦运行会立马执行。
- 在watchEffect中只能访问修改后的值,访问不了修改前的值。
场景
例如:当进入组件的时候需要开启定时器,离开组件的时候需要清空定时器,这个时候常规的做法是在挂载组件的生命周期中开启定时器,销毁组件的生命周期中清除定时器,而开启和销毁的逻辑并在一起,不便于阅读和维护,可以使用watchEffect解决。
watchEffect((onInvalidate) => {
const timer = setInterval(() => {
console.log('执行定时器');
}, 1000)
onInvalidate(() => clearInterval(timer))
})
转载自:https://juejin.cn/post/7254147431890960442