前言🌟
本文以实验的形式,为大家揭示Vue3中watch的最佳实践。
这篇文章的主要目的是研究watch在监听响应式数据时,如何获取当前值和先前值。顺便给大家补习一下watch的用法,然后引出为了配合watch能获取当前值和先前值,如何选用ref和reactive定义响应式数据。
一、API介绍
watch(WatcherSource, Callback, [WatchOptions])
type WatcherSource<T> = Ref<T> | (() => T)
interface WatchOptions extends WatchEffectOptions {
deep?: boolean // 默认:false
immediate?: boolean // 默认:false
}
参数说明:
WatcherSource: 用于指定要侦听的响应式变量。WatcherSource可传入ref响应式数据,reactive响应式对象要写成函数的形式。
Callback: 执行的回调函数,可依次接收当前值newValue,先前值oldValue作为入参。
WatchOptions:支持 deep、immediate。当需要对响应式对象进行深度监听时,设置deep: true;默认情况下watch是惰性的,当我们设置immediate: true时,watch会在初始化时立即执行回调函数。
除此之外,vue3的watch还支持侦听多个响应式数据,也能手动停止watch监听。
二、监听多个数据源
<template>
<div class="watch-test">
<div>name:{{name}}</div>
<div>age:{{age}}</div>
</div>
<div>
<button @click="changeName">改变名字</button>
<button @click="changeAge">改变年龄</button>
</div>
</template>
<script>
import {ref, watch} from 'vue'
export default {
name: 'Home',
setup() {
const name = ref('小松菜奈')
const age = ref(25)
const watchFunc = watch([name, age], ([name, age], [prevName, prevAge]) => {
console.log('newName', name, 'oldName', prevName)
console.log('newAge', age, 'oldAge', prevAge)
if (age > 26) {
watchFunc() // 停止监听
}
},{immediate:true})
const changeName = () => {
name.value = '有村架纯'
}
const changeAge = () => {
age.value += 2
}
return {
name,
age,
changeName,
changeAge
}
}
}
</script>
现象:当改变名字和年龄时,watch都监听到了数据的变化。当age大于26时,我们停止了监听,此时再改变年龄,由于watch的停止,导致watch的回调函数失效。
结论:我们可以通过watch侦听多个值的变化,也可以利用给watch函数取名字,然后通过执行名字()函数来停止侦听。
三、侦听数组
<template>
<div class="watch-test">
<div>ref定义数组:{{arrayRef}}</div>
<div>reactive定义数组:{{arrayReactive}}</div>
</div>
<div>
<button @click="changeArrayRef">改变ref定义数组第一项</button>
<button @click="changeArrayReactive">改变reactive定义数组第一项</button>
</div>
</template>
<script>
import {ref, reactive, watch} from 'vue'
export default {
name: 'WatchTest',
setup() {
const arrayRef = ref([1, 2, 3, 4])
const arrayReactive = reactive([1, 2, 3, 4])
//ref not deep
const arrayRefWatch = watch(arrayRef, (newValue, oldValue) => {
console.log('newArrayRefWatch', newValue, 'oldArrayRefWatch', oldValue)
})
//ref deep
const arrayRefDeepWatch = watch(arrayRef, (newValue, oldValue) => {
console.log('newArrayRefDeepWatch', newValue, 'oldArrayRefDeepWatch', oldValue)
}, {deep: true})
//reactive,源不是函数
const arrayReactiveWatch = watch(arrayReactive, (newValue, oldValue) => {
console.log('newArrayReactiveWatch', newValue, 'oldArrayReactiveWatch', oldValue)
})
// 数组监听的最佳实践- reactive且源采用函数式返回,返回拷贝后的数据
const arrayReactiveFuncWatch = watch(() => [...arrayReactive], (newValue, oldValue) => {
console.log('newArrayReactiveFuncWatch', newValue, 'oldArrayReactiveFuncWatch', oldValue)
})
const changeArrayRef = () => {
arrayRef.value[0] = 6
}
const changeArrayReactive = () => {
arrayReactive[0] = 6
}
return {
arrayRef,
arrayReactive,
changeArrayRef,
changeArrayReactive
}
}
}
</script>
现象:当将数组定义为响应式数据ref时,如果不加上deep:true,watch是监听不到值的变化的;而加上deep:true,watch可以检测到数据的变化,但老值和新值一样,即不能获取老值。当数组定义为响应式对象时,不作任何处理,watch可以检测到数据的变化,但老值和新值一样;如果把watch的数据源写成函数的形式并通过扩展运算符克隆一份数组返回,就可以在监听的同时获得新值和老值。
结论:定义数组时,最好把数据定义成响应式对象reactive,这样watch监听时,只需要把数据源写成函数的形式并通过扩展运算符克隆一份数组返回,即可在监听的同时获得新值和老值。
四、侦听对象
<template>
<div class="watch-test">
<div>user:{</div>
<div>name:{{objReactive.user.name}}</div>
<div>age:{{objReactive.user.age}}</div>
<div>}</div>
<div>brand:{{objReactive.brand}}</div>
<div>
<button @click="changeAge">改变年龄</button>
</div>
</div>
</template>
<script>
import {ref, reactive, watch} from 'vue'
import _ from 'lodash';
export default {
name: 'WatchTest',
setup() {
const objReactive = reactive({user: {name: '小松菜奈', age: '20'}, brand: 'Channel'})
//reactive 源是函数
const objReactiveWatch = watch(() => objReactive, (newValue, oldValue) => {
console.log('objReactiveWatch')
console.log('new:',JSON.stringify(newValue))
console.log('old:',JSON.stringify(oldValue))
})
//reactive,源是函数,deep:true
const objReactiveDeepWatch = watch(() => objReactive, (newValue, oldValue) => {
console.log('objReactiveDeepWatch')
console.log('new:',JSON.stringify(newValue))
console.log('old:',JSON.stringify(oldValue))
}, {deep: true})
// 对象深度监听的最佳实践- reactive且源采用函数式返回,返回深拷贝后的数据
const objReactiveCloneDeepWatch = watch(() => _.cloneDeep(objReactive), (newValue, oldValue) => {
console.log('objReactiveCloneDeepWatch')
console.log('new:',JSON.stringify(newValue))
console.log('old:',JSON.stringify(oldValue))
})
const changeAge = () => {
objReactive.user.age = 26
}
return {
objReactive,
changeAge
}
}
}
</script>
现象:当把对象定义为响应式对象reactive时,采用函数形式的返回,如果不加上deep:true,watch是监听不到值的变化的;而加上deep:true,watch可以检测到数据的变化,但老值和新值一样,即不能获取老值;若把watch的数据源写成函数的形式并通过深拷贝克隆(这里用了lodash库的深拷贝)一份对象返回,就可以在监听的同时获得新值和老值。
结论:定义对象时,最好把数据定义成响应式对象reactive,这样watch监听时,只需要把数据源写成函数的形式并通过深拷贝克隆一份对象返回,即可在监听的同时获得新值和老值。
五、总结✨
1.通常我们把原始类型的数据(number、string等)定义为ref响应数据,引用类型的数据(数组、对象)定义为reactive响应式数据;
2.当我们使用watch监听数据变化需要同时获取新值和老值时,我们需要把数据源定义为函数的形式,并且把数据源进行深拷贝返回。当我们只需要新值时,可以增加deep:true选项即可。
其实,引用类型的数据定义为ref形式也没关系,也只需要把数据源定义为函数的形式,并且把数据源进行深拷贝返回,便可获得新老值~哈哈哈哈哈哈哈哈哈哈哈哈哈哈,但我觉得最佳实践还是要把引用类型定义为reactive响应式数据。
到此这篇关于Vue3中watch的用法与最佳实践指南的文章就介绍到这了,更多相关Vue3中watch用法内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论