Vue3监听props变化必须用getter函数包装字段,如watch(() => props.count, ...),否则传值会导致响应性丢失;对象或数组需加{deep: true}才能捕获内部变更。

直接用 watch 监听 props 变化本身不难,但视图不刷新、回调不触发、深层数据没反应——这些问题几乎都出在“监听源写错了”或“响应性断了”。核心就一条:Vue 必须能追踪到变化的源头,而 props 里的字段不是天然可追踪的引用。
Vue 3:必须用 getter 函数包装 props 字段
Vue 3 的 props 是响应式代理对象,但 props.xxx 本身不是 Ref。直接传值进去,watch 就只拿到一个快照,后续变化它根本看不见。
- ✅ 正确写法:
watch(() => props.count, (newVal) => { /* 执行逻辑 */ }) - ❌ 错误写法:
watch(props.count, (newVal) => { ... })(监听的是初始值,非响应源) - 监听对象或数组时,加上
{ deep: true }才能捕获内部变更,比如:watch(() => props.config, (newVal) => { ... }, { deep: true })
Vue 2:可直接监听字段名,但注意 deep 和初始化陷阱
Vue 2 中 props 属性本身就是响应式的,watch 选项里可以直接写字段名。
- 简单类型(String/Number/Boolean):直接监听,如
count(newVal, oldVal) - 对象或数组需检测深层变动:启用
deep: true,但要留意性能,尤其数据量大时 - 避免首次加载误触发:加守卫判断,例如
if (newVal === undefined || newVal === oldVal) return
监听 ref 类型 props 要小心 .value
如果父组件传的是 :list="tableData",而 tableData 是个 ref([]),那子组件接收到的 props.list 就是那个 ref 对象本身,不是数组。
- 想监听数组内容变化:用
watch(() => props.list?.value, (newArr) => { ... }, { deep: true }) - 更推荐做法:父组件传
:list="tableData.value",子组件 props 定义为list: Array,再直接watch(() => props.list, ...)
为什么 deep: true 有时也不管用?
常见于父组件用 computed 返回结构相同的对象(比如每次返回新对象但字段一样)。Vue 可能复用响应式代理,导致引用地址没变,watch 认为“顶层没动”,deep 就不会启动。
- 破解方式:坚持用 getter 函数,强制每次取最新值,如
watch(() => props.optionData, ...) - 避免父组件反复赋同一个对象引用;必要时用
JSON.parse(JSON.stringify(obj))或structuredClone创建新引用 - 不需要深度监听全部字段?改用
computed派生关键状态,比 deep watch 更轻量可控

















