Vue 3 中子组件不能直接修改 props 的 reactive 对象,应通过自定义事件(如 update:user)通知父组件更新;嵌套对象需浅拷贝避免引用污染,或用 structuredClone 深拷贝;v-model 可实现字段级双向绑定,ref 包裹的对象同样不可直接修改。

不能直接修改。Vue 3 遵循单向数据流原则,子组件中接收到的 reactive 对象只是父组件数据的引用,直接改其属性(如 props.user.name = '李四')虽可能“生效”,但会触发 Vue 警告,且破坏响应式追踪逻辑、导致调试困难、违背设计规范。
正确做法:通过事件通知父组件更新
核心思路是——子组件不改 props,而是把“想改成什么样”告诉父组件,由父组件负责响应式更新源数据。
- 父组件用
reactive定义对象(例如form = reactive({ name: '张三', age: 25 })),并以 prop 形式传给子组件 - 子组件用
defineProps接收,但仅用于读取;所有修改操作都基于副本或计算逻辑生成新值 - 子组件通过
defineEmits声明自定义事件(如update:user),调用emit('update:user', newUser)把新对象传回去 - 父组件监听该事件,用
Object.assign(form, payload)或解构赋值(form.name = payload.name等)更新源数据
避免引用污染:嵌套对象要浅拷贝
如果传入的是嵌套对象(如 { user: { profile: { city: '杭州' } } }),子组件在构造新对象时,必须对嵌套层级做浅拷贝,否则仍共享引用:
- ❌ 错误:
const newUser = { ...props.user, profile: props.user.profile }→profile还是原引用 - ✅ 正确:
const newUser = { ...props.user, profile: { ...props.user.profile, city: '北京' } } - 复杂结构可用
structuredClone(现代浏览器)或lodash.cloneDeep做深拷贝
替代方案:用 v-model 封装对象字段
若需高频修改某几个字段(如表单编辑),可配合 v-model 语法糖 + computed 实现字段级双向绑定:
立即学习“前端免费学习笔记(深入)”;
- 父组件写:
<Child v-model:name="form.name" v-model:age="form.age" /> - 子组件接收
name和age两个 prop,并分别定义update:name和update:age事件 - 每个字段用
computed({ get, set })包装,set 中只 emit 对应事件,不触碰 props 本身
特别注意:ref 与 reactive 的使用边界
如果父组件用 ref 包裹对象(userRef = ref({ name: '张三' })),子组件接收后依然不能直接改 props.userRef.name —— 因为 ref 的响应式依赖于 .value 层级,而 props 是只读代理。正确方式仍是事件回传,或让父组件暴露一个更新函数作为 prop 传入(不推荐,耦合度高)。


















