You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中子组件修改数据为何会触发父组件更新?

子组件修改非嵌套属性为何同步更新父组件?

父组件代码

<template>
    <h2>Parent</h2>
    {{ parent.a }}
    {{ parent.b }}
    <ChildComponent :data="parent" />
</template>

<script setup>
import { reactive } from 'vue'
import ChildComponent from './components/ChildComponent.vue'
const parent = reactive({ a: 1, b: 2 })
</script>

子组件代码

<template>
    <h2>Child component</h2>
    <p>{{ child.a }}</p>
    <p>{{ child.b }}</p>
    <input type="text" v-model="child.b" />
</template>

<script setup>
import { reactive } from 'vue'
const props = defineProps(['data'])
const child = reactive(props.data)
child.a = 'why do i update the parent?'
</script>

疑问

原本以为props是单向绑定,必须通过emit把数据回传给父组件,但子组件修改child对象时,父组件的parent对象也跟着更新了。Vue文档里提到:

当对象或数组作为 props 传递时,子组件虽然不能修改 props 的绑定关系,但可以修改对象或数组的嵌套属性。这是因为在 JavaScript 中,对象和数组是按引用传递的,Vue 要阻止这类修改的代价过高。

但我的示例里a和b都不是嵌套属性,为什么还会出现这种情况?

原因解析

  • 你在子组件里执行const child = reactive(props.data)时,并没有创建新对象——props.data本身就是父组件的parent对象(引用类型),reactive只是把这个已有对象包装成响应式,child和parent指向的是内存里的同一个对象。
  • 不管是修改顶层属性(比如a、b)还是嵌套属性,本质都是在操作同一个引用的对象,父组件的parent自然会同步变化。
  • 文档里提到的“嵌套属性”只是这类引用传递场景的常见例子,不是说只有嵌套属性才会触发——只要传递的是对象/数组这类引用类型,修改它的任何层级属性都会影响原对象。

正确的解决方案

如果要避免子组件直接修改父组件状态,推荐以下几种方式:

1. 深拷贝原对象后再创建响应式

<script setup>
import { reactive } from 'vue'
// 可以用JSON序列化实现简易深拷贝,或者使用lodash的cloneDeep
const props = defineProps(['data'])
const child = reactive(JSON.parse(JSON.stringify(props.data)))
// 修改child就不会影响父组件的parent了
child.a = '只会修改子组件自己的对象'
</script>

2. 用toRefs拆分属性后创建独立响应式对象

<script setup>
import { toRefs, reactive } from 'vue'
const props = defineProps(['data'])
// 把props.data的属性转为独立ref
const { a, b } = toRefs(props.data)
// 创建新的响应式对象,基于原属性的值
const child = reactive({
  a: a.value,
  b: b.value
})
</script>

3. 遵循单向数据流,用emit通知父组件修改

// 子组件
<script setup>
import { ref } from 'vue'
const props = defineProps(['data'])
const emit = defineEmits(['update-data'])

// 子组件内部维护独立的响应式变量
const childA = ref(props.data.a)
const childB = ref(props.data.b)

// 修改时触发emit通知父组件
const handleUpdateB = () => {
  emit('update-data', { ...props.data, b: childB.value })
}
</script>

<template>
  <h2>Child component</h2>
  <p>{{ childA }}</p>
  <p>{{ childB }}</p>
  <input type="text" v-model="childB" @input="handleUpdateB" />
</template>
// 父组件接收emit
<template>
  <h2>Parent</h2>
  {{ parent.a }}
  {{ parent.b }}
  <ChildComponent :data="parent" @update-data="parent = $event" />
</template>

内容的提问来源于stack exchange,提问作者paddyfields

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 09:20:22