解构 props 会让值“变死”是因为 JavaScript 解构会提取当前时刻的静态值,脱离 Proxy 响应式连接;而点访问 props.dataId 每次触发 get 拦截器,保持响应式依赖追踪。

为什么解构 props 会让值“变死”
Vue 3 的响应式靠 Proxy 实现,props 是一个被 Proxy 包裹的响应式对象。当你写 const { dataId } = defineProps(),JavaScript 立刻把当前时刻的 dataId 值取出来,存进一个普通变量里——它和原始 props 再无关联。父组件改了 dataId,这个局部变量不会更新,模板也不会重渲染。
点访问 vs 解构:差别就在这一次“连接”
你在模板里写 {{ props.dataId }} 或脚本里写 props.dataId,每次读取都会触发 Proxy 的 get 拦截器,Vue 就能记住“这个副作用依赖 dataId”。但解构是一锤子买卖:只读一次、赋一个值、后续全靠这个静态快照——系统根本不知道它该响应谁的变化。
安全拆包的两种靠谱做法
-
推荐方式一:不拆,直接用
在 script setup 中始终通过
props.dataId访问;模板中也写{{ props.dataId }}或用defineModel/computed封装逻辑,不碰解构。 -
推荐方式二:用 toRefs(仅限选项式或需暴露多个 prop 到 return)
import { toRefs } from 'vue',然后const { dataId } = toRefs(props)—— 这会把每个 prop 转成独立的 ref,解构后仍可响应。注意:toRefs对基础类型(如 number、string)有效,但对 undefined/null 不会创建 ref,需额外判断。
watch 和初始化别踩坑
即使你没解构,也要注意:
- 直接在 setup 顶层写 console.log(props.dataId) 只能拿到初始值,不是响应式读取;
- 需要响应式监听,请用 watch(() => props.dataId, ...),加 { immediate: true } 才能捕获首次传入的值;
- 不要用 watch(props, ...) 监听整个 props 对象,它默认浅监听,且对基础类型 prop 无效。


















