在 Vue 3 中,实现父子组件间双向同步响应式对象的步骤如下:
实现思路
-
父组件 通过
v-model
传递响应式对象。 -
子组件接收并深拷贝为本地副本。
-
子组件监听父组件的数据变化,更新本地副本(不触发同步)。
-
子组件监听本地副本的变化,触发同步事件(排除父组件更新引起的变更)。
-
使用标志位
isUpdatingFromParent
区分变更来源,避免循环更新。
代码实现
父组件
vue
<template>
<Child v-model="parentData" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const parentData = ref({
name: 'Parent',
info: { id: 1 }
})
</script>
子组件
vue
<template>
<input v-model="localCopy.name">
<input v-model="localCopy.info.id" type="number">
</template>
<script setup>
import { ref, watch, nextTick } from 'vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:modelValue'])
// 深拷贝创建本地副本
const localCopy = ref(JSON.parse(JSON.stringify(props.modelValue)))
// 标志位:是否来自父组件的更新
let isUpdatingFromParent = false
// 监听父组件数据变化
watch(
() => props.modelValue,
async (newVal) => {
isUpdatingFromParent = true
localCopy.value = JSON.parse(JSON.stringify(newVal)) // 深拷贝更新
await nextTick()
isUpdatingFromParent = false
},
{ deep: true }
)
// 监听本地副本变化
watch(
localCopy,
(newVal) => {
if (!isUpdatingFromParent) {
emit('update:modelValue', JSON.parse(JSON.stringify(newVal)))
}
},
{ deep: true }
)
</script>
关键点说明
-
深拷贝处理 :使用
JSON.parse(JSON.stringify())
确保副本与原数据无引用关系。 -
双向监听机制:
-
父 → 子:监听
props.modelValue
,更新副本时标记来源,避免触发同步。 -
子 → 父:监听
localCopy
,非父组件更新时触发同步事件。
-
-
防循环处理 :通过
isUpdatingFromParent
和nextTick()
确保父组件更新不会触发子组件同步。
注意事项
-
性能优化 :深拷贝不适合大型对象,可改用
structuredClone
或工具库(如 lodash 的_.cloneDeep
)。 -
响应式类型 :如果父组件使用
reactive()
,需确保props.modelValue
是响应式引用。 -
结构稳定性:对象结构变化(增删属性)时,需重新深拷贝避免数据不一致。