标准答案

  1. defineModel 是 Vue 3 中用于声明组件 v-model 的编译器宏,可以少写 modelValue prop 和 update:modelValue emit 的样板代码。
  2. 默认 defineModel() 对应 modelValue / update:modelValue,传入名称时可以声明具名 v-model,例如 defineModel("visible")。
  3. 它返回一个 ref,组件内部修改这个 ref,会向父组件发出对应的 update 事件。
  4. 默认值要谨慎。如果子组件 defineModel 设置 default,而父组件没有传值,父子状态可能出现初始不同步。
  5. 组件是否受控要明确。表单和弹窗这类组件通常应让父组件作为状态来源,子组件通过 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 状态也被父组件控制,接口变复杂。

作者信息