首页 / Vue3 入门教程 / 组件 v-model

Vue3 入门教程

组件 v-model

本教程共 40 篇 · 第 20 篇 · 更新于 2026-07-29 · 约 8 分钟阅读

VueVue3 入门教程v-modeldefineModel双向绑定修饰符

20. 组件 v-model

本节目标:掌握在组件上使用 v-model 的方法,学会 defineModel 简化双向绑定。

v-model 在组件上意味着什么

原生 <input v-model="text">:value + @input 的语法糖。组件上的 v-model 同理——它让父组件和子组件的数据保持自动同步。

defineModel——Vue 3.4 的新方式

<script setup> 中,用 defineModel() 宏声明一个 v-model 绑定的值:

<!-- Child.vue -->
<script setup>
const model = defineModel()

function update() {
  model.value++
}
</script>

<template>
  <div>父组件绑定的值: {{ model }}</div>
  <button @click="update">加一</button>
</template>

父组件直接用 v-model:

<Child v-model="countModel" />

defineModel() 返回一个 ref。它的 .value 和父组件绑定的值保持双向同步:

  • 父组件改了值 → 子组件的 ref 跟着变
  • 子组件改了 ref → 父组件的值跟着变

原理拆解

defineModel 是语法糖,实际等价于:

  1. 声明一个名为 modelValue 的 prop
  2. 声明一个名为 update:modelValue 的事件

之前要这么写:

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="props.modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

现在一行 defineModel() 搞定。

带选项的 defineModel

// 必填
const model = defineModel({ required: true })

// 带默认值
const model = defineModel({ default: 0 })
Warning

如果 defineModel 的 prop 有默认值,但父组件没传值,父子会不同步。父组件是 undefined,子组件是默认值。

带参数的 v-model

一个组件可以有多个 v-model,用参数区分:

<MyComponent v-model:title="bookTitle" />

子组件用字符串参数:

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>

<template>
  <input type="text" v-model="title" />
</template>

需要传选项就在后面加对象:

const title = defineModel('title', { required: true })

多个 v-model 绑定

一个组件同时支持多个 v-model:

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>

子组件:

<!-- UserName.vue -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
</template>

每个 v-model 独立同步,互不干扰。

自定义修饰符

v-model 支持 .trim.number 等内置修饰符。组件 v-model 也能自定义修饰符。

定义一个 capitalize 修饰符,把首字母大写:

<MyComponent v-model.capitalize="myText" />

子组件通过解构拿到修饰符对象:

<script setup>
const [model, modifiers] = defineModel()

console.log(modifiers) // { capitalize: true }
</script>

根据修饰符决定是否转换值:

<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<template>
  <input type="text" v-model="model" />
</template>
Tip

defineModel() 返回的 ref 可以解构为 [model, modifiers]。modifiers 是一个对象,键是修饰符名,值是布尔值。

多个 v-model 各自的修饰符

不同 v-model 可以有各自的修饰符:

<UserName
  v-model:first-name.capitalize="first"
  v-model:last-name.uppercase="last"
/>
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')

console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>

本节回顾

  • defineModel() 是组件 v-model 的推荐写法(3.4+)
  • 它内部创建了一个 prop 和一个 update 事件
  • 用参数创建命名 v-model,支持多个绑定
  • 自定义修饰符通过解构获取,set 选项可以转换值
  • 让自定义表单组件也能像原生 input 一样使用 v-model