标准答案

  1. props 遵循单向数据流。父组件的数据向下传给子组件,子组件直接修改 props 会破坏父组件作为状态来源的约定。
  2. Vue 在开发环境会对直接修改 props 给出警告。即使某些引用类型内部属性能被改,也会让数据来源不清楚,后续调试困难。
  3. 子组件需要通知父组件修改时,可以 emit 事件,例如 update:value,父组件接到事件后更新自己的状态。
  4. 封装表单组件时,Vue 3 常用 v-model 或 defineModel 表达受控值;Vue 2 常见做法是 value + input 事件,或自定义 model。
  5. 如果子组件只需要一个初始值,可以用本地 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 后,忘记父组件后续变化不会自动覆盖本地编辑状态。

作者信息