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