以下是针对 父组件传递对象 + 子组件修改对象属性 的完整示例代码,同时遵守 Vue3 的单向数据流原则:
1. 父组件代码 (ParentComponent.vue)
vue
<template> <!-- 通过 v-model 传递整个对象 --> <ChildComponent v-model="formData" /> <!-- 显示数据变化 --> <div>父组件数据:{{ formData }}</div> </template> <script setup> import { reactive } from 'vue'; import ChildComponent from './ChildComponent.vue'; // 父组件数据(包含多个属性) const formData = reactive({ name: '张三', age: 25, address: { city: '北京', street: '朝阳区' }, email: 'zhangsan@example.com' }); </script>
2. 子组件代码 (ChildComponent.vue)
vue
<template> <div> <!-- 绑定对象属性 --> <el-input v-model="localForm.name" placeholder="姓名" /> <el-input v-model="localForm.age" placeholder="年龄" type="number" /> <el-input v-model="localForm.address.city" placeholder="城市" /> <el-input v-model="localForm.email" placeholder="邮箱" /> </div> </template> <script setup> import { computed } from 'vue'; const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); // 核心:通过计算属性实现双向绑定 const localForm = computed({ get: () => props.modelValue, set: (newValue) => { // 触发更新事件,通知父组件 emit('update:modelValue', newValue); } }); </script>
3. 方案解析
为什么能直接修改对象属性?
响应式原理:父组件通过
reactive()
创建响应式对象,子组件通过computed
的set
方法触发更新。数据流安全:虽然看似直接修改了对象属性,但实际上是通过
emit
让父组件更新自己的数据,符合单向数据流。
注意事项
不要直接赋值整个对象:
js
// ❌ 错误!这会导致失去响应性 localForm.value = { ...localForm.value, name: '李四' }; // ✅ 正确!Vue 会追踪对象内部属性的变化 localForm.value.name = '李四';
嵌套对象更新:如果修改深层属性(如
address.city
),需确保父组件对象是reactive
创建的。
4. 扩展:使用 defineModel
(Vue 3.4+)
如果使用 Vue 3.4+ 的 defineModel,可以进一步简化代码:
vue
<script setup> // 子组件 (ChildComponent.vue) const model = defineModel(); // 自动处理 v-model 绑定 // 直接修改属性即可(底层自动触发 emit) const handleChange = () => { model.value.name = '李四'; }; </script>
5. 最终效果
父组件数据会随着子组件的修改自动更新。
所有修改都通过 Vue 的响应式系统追踪,无直接操作 props 的风险。
常见问题解决
Q:为什么修改子组件后父组件数据没变?
确保父组件数据用
reactive
或ref
创建。不要解构父组件对象(如
const { name } = formData
会失去响应性)。
Q:如何修改嵌套对象属性?
直接修改即可(得益于 Vue 的响应式系统):
js
// 子组件中 localForm.value.address.city = '上海'; // 自动触发更新
通过这个方案,你可以在保持代码简洁的同时,安全地实现 父子组件间复杂对象的双向绑定。