一、watch 监听响应式式数据;可以监听多个;
语法:watch(source, callback, options)
### source 可以是下面这几种情况:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- ...或是由以上类型的值组成的数组
### callback 数据源改变时的回调,可以检测新旧值得变化
### options: 配置项: immediate/deep/flush/onTrack/onTrigger
示例:
2.1 监听一个或多个ref;无坑
const foo = ref('foo');
const bar = ref(‘bar’)
//1、 监听一个ref
watch(foo, (newFoo,preFoo) =>{
console.log('newFoo->',newFoo,'preFoo->',preFoo)
})
//2、 监听多个ref
watch([foo,bar], ([newFoo,newBar],[preFoo,preBar]) =>{
console.log('newFoo->',newFoo,'preFoo->',preFoo)
console.log('newBar->',newBar,'preBar->',preBar)
})
2.2 监听一个ref类型的响应式对象;有坑:需要开启deep;同时存在问题;新旧值相同;无法检测旧值
const nested = ref({
foo:{
bar: 1
},
baz: 2
})
// 注意:ref包裹引用类型,默认监听不到,需要开启deep; 但开启之后,新值和旧值一样,无法监听旧值
watch(nested, (newNested,preNested) =>{
console.log('newNested->',newNested,'preNested->',preNested)
},{
deep: true //需要手动开启
})
2.3 监听一个reactive对象,默认开启deep;但仍存在2.2上述问题;无法检测旧值
const foobar = reactive({
foo: 1,
bar:{
baz:2
}
watch(foobar, (foobar,preFoobar) =>{
console.log('foobar->',foobar,'preFoobar->',preFoobar)
})
😇: 如果上面写成getter函数形式,需要手动开启deep;但仍存在2.2上述问题;无法检测旧值
watch(() => foobar, (foobar,preFoobar) =>{
console.log('foobar->',foobar,'preFoobar->',preFoobar)
},{deep: true})
2.4、监听一个reactive对象的多个属性,以及嵌套属性;source必须是函数返回一个值,即getter函数;无坑
watch([() => foobar.foo,() => foobar.bar.baz], ([newFFoo,newFBaz],[preFFoo,preFBaz]) =>{
console.log('newFFoo->',newFFoo,'preFFoo->',preFFoo)
console.log('newFBaz->',newFBaz,'preFBaz->',preFBaz)
})
2.6 完整示例
<template>
<div id="app">
<h3>watch</h3>
<p><strong>说明: watch 显式指定依赖源,依赖源更新时执行回调函数</strong></p>
<h5>监听一个或者多个ref</h5>
<div>
foo:{{foo}} bar:{{bar}}
<button @click="foo+='~';bar+='~'">修改</button>
</div>
<h5>监听一个ref类型的响应式对象</h5>
{{nested}}
<button @click="nested.baz++">修改nested</button>
<h5>监听一个reactive的响应式对象</h5>
{{foobar}}
<button @click="foobar.foo++;foobar.bar.baz++">修改foobar</button>
</div>
</template>
<script setup>
import { defineComponent, ref, watch, reactive } from "vue";
const foo = ref('foo')
const bar = ref('bar')
const nested = ref({
foo:{
bar: 1
},
baz: 2
})
const foobar = reactive({
foo: 1,
bar:{
baz:2
}
})
//1、 监听一个ref
watch(foo, (newFoo,preFoo) =>{
console.log('newFoo->',newFoo,'preFoo->',preFoo)
})
//2、 监听多个ref
watch([foo,bar], ([newFoo,newBar],[preFoo,preBar]) =>{
console.log('newFoo->',newFoo,'preFoo->',preFoo)
console.log('newBar->',newBar,'preBar->',preBar)
})
//3、 注意:ref包裹引用类型,默认监听不到,需要开启deep; 但开启之后,新值和旧值一样,无法监听旧值
watch(nested, (newNested,preNested) =>{
console.log('newNested->',newNested,'preNested->',preNested)
},{
deep: true
})
//4、 监听一个reactive对象,默认开启deep;新旧值相同,无法监听旧值
watch(foobar, (foobar,preFoobar) =>{
console.log('foobar->',foobar,'preFoobar->',preFoobar)
})
// 注意:如果写成getter函数,必须开启deep;但同样存在如上问题,无法监听旧值
watch(() => foobar, (foobar,preFoobar) =>{
console.log('foobar->',foobar,'preFoobar->',preFoobar)
},{deep: true})
//5、监听一个reactive对象的多个属性,以及嵌套属性;source必须是函数返回一个值
watch([() => foobar.foo,() => foobar.bar.baz], ([newFFoo,newFBaz],[preFFoo,preFBaz]) =>{
console.log('newFFoo->',newFFoo,'preFFoo->',preFFoo)
console.log('newFBaz->',newFBaz,'preFBaz->',preFBaz)
})
</script>
<style scoped></style>
二、watchEffect :自动收集依赖;依赖源更新自动触发;始终是最新的值
<button @click="nested.baz++">修改nested</button>
const nested = ref({
foo:{
bar: 1
},
baz: 2
})
watchEffect(() =>{
console.log('fooE->',foo) √
console.log('nestedE->',nested.value.baz) √
console.log('foobarE->',foobar.foo,foobar.bar.baz) √
// console.log(nested) nested可检测不到,检测不到外部变化; nested.baz变化;就直接访问变化的值;
})
三、watch 与watchEffect区别
watch 懒执行,显式指定依赖源,依赖源更新时执行回调函数
watchEffect,立即执行,依赖源更新自动触发,始终返回最新的值
相同点:具有相同的享有相同的刷新时机和调试选项;副作用清除和停止监听用法相同
watch(source, callback, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
### 停止侦听
const stop = watch(source, callback)
// 当已不再需要该侦听器时:
stop()
### 副作用清除
watch(id, async (newId, oldId, onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 当 `id` 变化时,`cancel` 将被调用,
// 取消之前的未完成的请求
onCleanup(cancel)
data.value = await response
})