标准答案
- defineModel 是 Vue 3 中用于声明组件 v-model 的编译器宏,可以少写 modelValue prop 和 update:modelValue emit 的样板代码。
- 默认 defineModel() 对应 modelValue / update:modelValue,传入名称时可以声明具名 v-model,例如 defineModel("visible")。
- 它返回一个 ref,组件内部修改这个 ref,会向父组件发出对应的 update 事件。
- 默认值要谨慎。如果子组件 defineModel 设置 default,而父组件没有传值,父子状态可能出现初始不同步。
- 组件是否受控要明确。表单和弹窗这类组件通常应让父组件作为状态来源,子组件通过 v-model 表达修改意图。
题目解析
defineModel 解决的是重复声明问题,不是改变 v-model 的数据流模型。
受控组件里,父组件仍然拥有最终状态。子组件改 model ref,本质上是发 update 事件。
默认值问题经常被忽略。父组件传入 undefined 时,子组件内部默认值看起来有值,但父组件状态并没有同步变成这个值。
代码示例
一个开关组件可以用 defineModel 把 modelValue 和 update 事件合并成一个 ref。
Vue
<!-- ToggleSwitch.vue -->
<script setup lang="ts">
const checked = defineModel<boolean>({ required: true })
</script>
<template>
<button type="button" @click="checked = !checked">
{{ checked ? '开启' : '关闭' }}
</button>
</template>Vue
<script setup lang="ts">
import { ref } from 'vue'
import ToggleSwitch from './ToggleSwitch.vue'
const enabled = ref(false)
</script>
<template>
<ToggleSwitch v-model="enabled" />
</template>常见误区
- 认为 defineModel 会让子组件直接修改父组件变量。它仍然通过 update 事件同步。
- 在受控组件里随意设置 default,导致父组件没传值时父子状态不一致。
- 所有状态都用 defineModel 暴露,组件内部临时 UI 状态也被父组件控制,接口变复杂。