标准答案
- props 遵循单向数据流。父组件的数据向下传给子组件,子组件直接修改 props 会破坏父组件作为状态来源的约定。
- Vue 在开发环境会对直接修改 props 给出警告。即使某些引用类型内部属性能被改,也会让数据来源不清楚,后续调试困难。
- 子组件需要通知父组件修改时,可以 emit 事件,例如 update:value,父组件接到事件后更新自己的状态。
- 封装表单组件时,Vue 3 常用 v-model 或 defineModel 表达受控值;Vue 2 常见做法是 value + input 事件,或自定义 model。
- 如果子组件只需要一个初始值,可以用本地 ref 接收 props 的初始值。此时要说清它是本地状态,不再自动等同于父组件状态。
题目解析
这道题的重点是状态所有权。谁拥有状态,谁负责修改状态。子组件可以表达用户操作,但最终状态应该由父组件或统一状态层更新。
对象和数组 props 最容易让人误解。直接改 user.name 可能不会立刻触发 props 只读警告,但父子边界已经被打穿。
双向绑定不是让子组件随便改父数据,而是用约定好的 prop 和 event 组成一个受控接口。
代码示例
这个例子展示子组件通过 update:modelValue 把输入变化交给父组件。
Vue
<!-- UserNameInput.vue -->
<script setup lang="ts">
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
function handleInput(event: Event) {
const target = event.target as HTMLInputElement
emit('update:modelValue', target.value)
}
</script>
<template>
<input :value="props.modelValue" @input="handleInput">
</template>Vue
<script setup lang="ts">
import { ref } from 'vue'
import UserNameInput from './UserNameInput.vue'
const userName = ref('')
</script>
<template>
<UserNameInput v-model="userName" />
</template>常见误区
- 在子组件里直接写 props.value = nextValue。
- 认为对象 props 可以随便改内部属性。这样会让父子组件共享可变状态,数据流变得不清楚。
- 把 props 复制成本地 ref 后,忘记父组件后续变化不会自动覆盖本地编辑状态。