最近写一个vue单文件的组件时遇到了监听失败的问题,总结一下:
一、直接解构defineProps
子组件 Child.vue
"vue": "^3.4.15"
<template>
<h1>子组件</h1>
<div>{{count}}</div>
</template>
<script setup>
import {watch} from 'vue'
const {count,data} = defineProps(['count','data'])
watch(
() => count,
() => {
console.log('watch count=', count)
},
{
deep: true,
immediate: true,
}
)
watch(
() => data.count,
() => {
console.log(' watch data=', data.count)
},
{
deep: true,
immediate: true,
}
)
</script>
父组件 Parent.vue
<template>
<h1>父组件</h1>
<Child count={count}/>
<button @click="onClick">点击</button>
</template>
<script setup>
import {ref,reactive} from 'vue'
import Child form './Child.vue'
const count = ref(0)
const data = reactive({count:0})
const onClick = ()=>{
count.value = count.value + 1
data.count = data.count + 1
}
</script>
结果:
- 页面可以渲染出count 和 data.count
- watch 只能监控到data.count的变化
原因:
defineProps返回的是 响应式 Proxy 对象- 解构 = 直接取值,相当于把值快照到普通变量
count变成了普通常量,不再关联 Proxy- 后续父组件更新,这个变量不会变,watch 自然监听不到
为什么 data.count 能监听到?
data是父组件传的 reactive 对象- 解构后拿到的是对象引用
- 访问
data.count依然走 对象自身的响应式 - 所以能监听到
- 这是巧合,不是正确用法!
二、使用props
子组件 Child.vue
<script setup>
import {watch} from 'vue'
const props = defineProps(['count','data'])
watch(
() => props.count,
() => {
console.log('watch count=', count)
},
{
deep: true,
immediate: true,
}
)
watch(
() => props.data.count,
() => {
console.log(' watch data=', data.count)
},
{
deep: true,
immediate: true,
}
)
</script>
结果:
- 页面可以渲染出count 和 data.count
- watch 可以监控到props.count 和 props.data.count的变化
三、使用 torefs 结构 props
子组件 Child.vue
<script setup>
import {toRefs,watch} from 'vue'
const props = defineProps(['count','data'])
const {count,data} = toRefs(props)
watch(
() => count,
() => {
console.log('watch count=', count.value)
},
{
deep: true,
immediate: true,
}
)
watch(
() => data.count,
() => {
console.log(' watch data=', data.count)
},
{
deep: true,
immediate: true,
}
)
watch(
() => data,
() => {
console.log(' watch data=', data.value)
},
{
deep: true,
immediate: true,
}
)
</script>
结果:
- 页面可以渲染出count 和 data.count
- watch 可以监控到count的变化,但是不能监测到data.count
- 此时count和data转换为了ref类型count和data的值需要用.value 读取出来
原因:
-
toRefs把 props 里的每个属性变成 独立 refcount→ ref 对象data→ ref 对象(里面包裹着原来的 reactive 对象)
watch(() => data.count, ...)
监听的是一个普通值,不是 ref,也不是 getter 不会建立依赖,所以监听不到
正确写法应该是:
watch(
() => data.value.count,
() => { ... },
{ deep: true }
)
或者直接监听 data 这个 ref:
watch(data, () => { ... }, { deep: true })