解构响应式数据会丢失响应性,因破坏 Proxy 代理链或脱离 ref 包裹;应使用 toRefs、toRef、storeToRefs 或直接访问原对象属性。

在组合式 API 中,解构响应式数据时最容易“悄无声息”地丢失响应性——表面看代码能跑、值能读,但修改不触发更新、watch 不执行、模板不重绘。问题不在数据消失,而在 Vue 的响应式追踪链被意外切断。
别直接解构 reactive 对象
reactive 创建的是一个 Proxy 代理对象,所有响应式能力都依赖这个代理层。一旦用 const { count } = state 解构,得到的就是普通 JS 值,脱离了 Proxy 控制。
- ❌ 错误写法:
const state = reactive({ count: 0 }); const { count } = state; count++;→ 视图不动 - ✅ 安全做法:保持原对象访问,
state.count++或改用toRefs(state) - ✅ 推荐模式:对结构化数据用
reactive,对外暴露统一走toRefs
解构 ref.value 要格外小心
ref({}) 是一个包裹对象的 ref,.value 才是内部原始对象。直接解构 user.value 就等于把响应式外壳剥掉。
- ❌ 危险操作:
const user = ref({ name: 'Alice' }); const { name } = user.value; name = 'Bob';→ 无响应 - ✅ 正确方式:
const { name } = toRefs(user.value),或直接user.value.name = 'Bob' - ? 小技巧:如果只用一两个字段,可用
toRef(user, 'name')避免创建多余 ref
props 解构是高频雷区
defineProps 返回的是响应式 props 代理对象,解构会立刻断开与父组件的响应连接。
- ❌ 绝对避免:
const { title, loading } = defineProps({ title: String })→ 后续 prop 更新,title 不变 - ✅ 推荐方案一:不解构,始终用
props.title - ✅ 推荐方案二:用
toRefs(defineProps(...)),再解构,const { title } = toRefs(props)
配合 storeToRefs 处理 Pinia
Pinia store 本身是 reactive 对象,直接解构 useUserStore() 同样失效。
- ❌ 错误:
const { user } = useUserStore()→ user 是普通对象,修改不响应 - ✅ 必须:
const { user, loading } = storeToRefs(useUserStore())→ 得到的是 ref,支持user.value.name和模板中{{ user.name }} - ⚠️ 注意:
storeToRefs只包装已存在于 store 中的响应式属性,动态添加的 key 不会被捕获

















