vue3 watch 最佳实践
项目中在监听数据变化,有时候不生效,又去翻阅文档,watch 和 watchEffect 如何选择才比较好?
这些问题,都挺关键的,现在总结一下比较好的做法。
watch
watch 的第一个参数是监听的数据源,有四种形式:
- ref:
ref
、computed
; - reactive;
- getter: 返回一值的函数;
- 以上三种组成的数组。
监听 ref 和 shallowRef
<script setup lang="ts">
const a = ref(0)
const b = ref(10)
watch(a, (a, oldA) => {
console.log(`watch(a),a:${a},oldA:${oldA}`)
})
// 监听不到 ❌ ts 也给出提示
watch(a.value, (a, oldA) => {
console.log(`watch(a),a:${a},oldA:${oldA}`)
})
watch([a, b], ([a, b]) => {
console.log(`watch([a, b] ,a:${a},b:${b}`)
})
const sum = computed(() => {
return a.value + b.value
})
watch(sum, sum => {
console.log(`watch(sum) ,sum:${sum}`)
})
watch([a, () => b.value + 1000], ([a, sum]) => {
console.log(`watch([a, () => b.value + 1000] a:${a} sum:${sum}`)
})
function changeA() {
a.value += 1
b.value += 10
}
const numList = shallowRef([1, 2, 3])
function changeNumList() {
// numList.value.push(10, 20, 30)
// NOTE shallowRef 的数组,使用 push pop 等方法修改 无法监测到 ❌
numList.value = [...numList.value, 10, 20, 30]
}
watch(numList, numList => {
console.log(numList, `watch(numList), numList: ${numList}`)
})
function changeNumList2() {
numList.value = ['10', '20', '30']
}
const shallowA = shallowRef({
a: {
b: {
c: 10,
},
},
d: 100,
})
watch(shallowA, val => {
console.log('watch(shallowA): ', val)
})
watch(
() => shallowA.value.a,
val => {
console.log('watch(() => shallowA.value.a): ', val)
}
)
function changeShallowA() {
shallowA.value = {
// @ts-ignore
e: 1000,
}
}
</script>
<template>
<div>
<h2>watch ref</h2>
<p>a:{
{ a }}</p>
<p>b:{
{ b }}</p>
<p>sum:{
{ sum }}</p>
<button @click="changeA">changeA</button>
<h2>watch shallowRef</h2>
<p>shallowA:{
{ shallowA }}</p>
<button @click="changeShallowA">changeShallowA</button>
<h2>watch array</h2>
<p>numList:{
{ numList }}</p>
<button @click="changeNumList">changeNumList</button>
<button @click="changeNumList2">changeNumList2</button>
</div>
</template>
最佳实践
-
监听 ref:
watch(ref)
或者watch(computedRef)
-
监听 shallowRef:
watch(ref)
或者watch(computedRef)
-
关于 ref和 shallowRef 的修改,使用
xxx.value = newValue
,即直接重置,可有效降低心智负担。
比如上面的数组添加,numList 是一个 shallowRef
,使用数组方法 numList.value.push(10, 20, 30)
添加元素,无法监测到 numList
的改变 。使用 push
、 pop
等改变数组类型的 ref,是能监听到的。
修改数组,推荐都这样改:
numList.value = [...numList.value, 10, 20, 30] // 直接重置数组 shallowRef 也是如此
修改对象某个属性:
shallowA.value = {
...shallowA.value,
a: {
b: 'this is a.b '
},
// @ts-ignore
e: 1000,
}
关于 reactive 和 shallowReactive 的监听
- 监听整个 reactive,直接写,
watch(person)
,不用添加{deep: true}
,是默认深度监听的; - 使用函数返回整个 reactive:
watch(()=>({..person}))
, 默认浅层监听的,监听深层属性,添加{deep:true}
; - 监听 reactive 的单个属性,使用函数返回,监听多个属性,使用数组:
watch(person.age) watch([()=>person.age, ()=>person.name])
不能直接侦听响应式对象的属性值
const obj = reactive({
count: 0
})
// 错误,因为 watch() 得到的参数是一个 number ❌
watch(obj.count, (count) => {
console.log(`count is: ${
count}`)
})
// 应该这样监听:提供一个 getter 函数 ✅
watch(
() => obj.count,
(count) => {
console.log(`count is: ${
count}`)
}
)
shallowReactive 只是第一层属性是响应式的,添加
{deep:true}
,也监听不到深层次属性。
watch 的第三个参数
watch 的第三个参数,是一个配置对象
watch(reactiveVariable, effectCallback, {
deep: true, // 监听深层属性
imme