Vue3 watch监听props需用getter函数包装,如watch(() => props.message, cb),因props.xxx非响应式引用;监听ref类型props需取.value;对象数组变化须加deep: true;避免重复赋相同引用。

Vue3 的 watch 监听 props 不生效,不是 watch 本身坏了,而是你“盯错了地方”。核心就一条:props 里的值必须被 Vue 当作响应式源来读取,而直接传 props.xxx 很可能只是个普通值或复用的代理对象——watch 根本抓不到变化。
必须用 getter 函数包装 props 属性
这是 90% 失效问题的根源。Vue3 的 watch(source, callback) 要求 source 是响应式引用(Ref)或能返回响应式值的函数。props.xxx 本身不是 Ref,哪怕它来自父组件的 ref,到了子组件 props 里也只是一个“被代理过的普通属性”。
所以正确写法只有一种主流形式:
-
✅ 正确:
watch(() => props.message, (newVal) => { ... }) -
❌ 错误:
watch(props.message, (newVal) => { ... })(原始值无依赖追踪) -
❌ 错误:
watch(props.obj, (newVal) => { ... }, { deep: true })(若 obj 是 computed 返回的复用代理,引用未变,deep 也白搭)
监听 ref 类型 props 要读 .value
如果父组件传的是 :list="tableData",而 tableData 是一个 ref([]),那子组件 props 接收后,props.list 实际上是那个 ref 对象本身(不是数组)。watch 若不取值,就等于在监听 ref 对象有没有被重新赋值,而不是监听数组内容变没变。
立即学习“前端免费学习笔记(深入)”;
解决方式有两种:
Element Plus Vue3 桌面组件库。在开发 Vue 3.x 页面时调用,使用 Element Plus 组件(如 el-button、el-input、el-form、el-table 等)。
- 监听其值:
watch(() => props.list?.value, (newArr) => { ... }, { deep: true }) - 更推荐:父组件直接传
.value,比如:list="tableData.value",子组件 props 定义为list: Array,再watch(props.list, ..., { deep: true })
对象/数组内部变化要加 deep: true
即使你用了 getter 函数,如果监听的是一个响应式对象(比如 reactive({ a: 1 })),默认只监听顶层引用是否更换。内部属性如 a 改了,watch 不会触发——除非显式开启深度监听。
- 监听 reactive 对象或数组内容变化:
watch(() => props.config, (newVal) => { ... }, { deep: true }) - 注意:
deep: true不能挽救“引用地址没变”的问题(如 computed 返回结构相同的对象),它只对已建立依赖的对象起效
避免 props 被重复赋相同值
Vue 的 watch 是基于值变化触发的。如果父组件多次把一模一样的对象或数组赋给同一个 prop(比如 data = { ... } 然后 props.data = data),Vue 判断引用或浅层值未变,就不会通知子组件更新,watch 自然不执行。
业务中常见于:
- computed 返回新对象但结构恒定(如
computed(() => ({ x: 1 }))每次都返回新对象,但 watch 可能因 Proxy 复用而失效) - 父组件未做防抖或状态合并,频繁重设相同数据
对策:在 watch 回调里加简单判空或 JSON.stringify 对比;或让父组件确保每次传递的都是“新引用”(如用 {...obj} 或 structuredClone)。
异步加载场景下 watch 是唯一可靠入口
父组件数据是接口异步获取的,子组件初次渲染时 props 还是初始值(如 [] 或 null)。这时候不能靠 onMounted 去取值——它只执行一次,且早于数据到达。
watch 就是为此而生:
- 只要 props 值真正变化(哪怕从
undefined → []),watch 就会触发 - 适合初始化图表、表格、表单回填等依赖真实数据的操作
- 必要时配合
nextTick确保 DOM 更新后再操作(如initChart()后需重绘)

















