reactive 的响应式是深度绑定 的(默认递归代理所有嵌套对象),直接解构外层对象得到的嵌套对象,本质还是 reactive 生成的代理对象,因此它本身的响应式不会丢失;但如果对这个嵌套对象再做解构,就会回到之前的问题 ------ 解构其属性会丢失响应式。
代码示例(核心验证)
vue
xml
<script setup lang="ts">
import { reactive } from 'vue'
// 创建响应式对象 const user = reactive({ name: '张三', age: 20 }) // 直接解构:丢失响应式 const { name, age } = user 对属性是基本类型时会丢失响应式这时需要用toRefs包裹
// 外层响应式对象,包含嵌套对象
const user = reactive({
info: { // 嵌套对象,被 reactive 深度代理
name: '张三',
age: 20
},
hobby: ['篮球', '游戏'] // 嵌套数组,同样被深度代理
})
const user = reactive({ name: '张三', age: 20 }) // 用 toRefs 解构:保留响应式(转为 ref 类型) const { name, age } = toRefs(user) const changeName = () => { name.value = '李四' // 需通过 .value 修改,原对象会同步更新 console.log(user.name) // 输出 "李四" }
直接解构外层对象得到的嵌套对象
// 直接解构外层对象:拿到嵌套对象 info 和 hobby
const { info, hobby } = user
// 场景1:修改解构出的嵌套对象的属性(仍有响应式)
const changeInfo = () => {
info.name = '李四' // ✅ 有响应式,视图会更新
hobby.push('看书') // ✅ 有响应式,视图会更新
console.log(user.info.name) // 输出 "李四"(和原对象同步)
}
// 场景2:对嵌套对象再解构(属性丢失响应式)
const { name, age } = info
const changeName = () => {
name = '王五' // ❌ 非响应式,TS 提示无法赋值,视图无变化
console.log(info.name) // 仍然是 "李四"
}
// 场景3:直接替换整个嵌套对象(仍有响应式)
const replaceInfo = () => {
info.age = 25 // ✅ 改属性:响应式
// 注意:如果直接替换整个嵌套对象,也需要通过原对象或解构的嵌套对象操作
user.info = { name: '赵六', age: 30 } // ✅ 响应式
// 或 info = { name: '赵六', age: 30 } ❌ 错误!解构的 info 是常量,不能直接赋值
}
</script>
<template>
<div>原对象:{{ user.info.name }} - {{ user.info.age }} | {{ user.hobby }}</div>
<div>解构嵌套对象:{{ info.name }} - {{ info.age }} | {{ hobby }}</div>
<div>解构嵌套对象的属性:{{ name }} - {{ age }}</div>
<button @click="changeInfo">修改嵌套对象属性</button>
<button @click="changeName">修改解构的嵌套属性</button>
<button @click="replaceInfo">替换嵌套对象</button>
</template>
运行结果:
- 点击「修改嵌套对象属性」:所有关联视图(原对象、解构的嵌套对象)都会更新;
- 点击「修改解构的嵌套属性」:视图无变化,嵌套对象的属性也没改;
- 点击「替换嵌套对象」:原对象和解构的嵌套对象视图都会更新。
二、原理拆解:为什么嵌套对象仍有响应式?
reactive对对象做深度代理 :当创建reactive({ info: { name: '张三' } })时,不仅外层对象被Proxy代理,内部的info对象也会被递归转为Proxy代理对象;- 直接解构
const { info } = user:拿到的info是reactive生成的代理对象本身 (而非原始值),因此访问 / 修改info.name仍会触发响应式的依赖收集和更新; - 解构嵌套对象的属性
const { name } = info:拿到的是info.name的原始值(如字符串 "张三"),而非代理属性,因此丢失响应式。