以下是针对 父组件传递对象 + 子组件修改对象属性 的完整示例代码,同时遵守 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: '[email protected]'
});
</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 = '上海'; // 自动触发更新
通过这个方案,你可以在保持代码简洁的同时,安全地实现 父子组件间复杂对象的双向绑定。