组件 v-model {#component-v-model}
基本用法 {#basic-usage}
v-model 可以在组件上使用以实现双向绑定。
从 Vue 3.4 开始,推荐的实现方式是使用 [`defineModel()`](/api/sfc-script-setup#definemodel) 宏:
```vue [Child.vue]
Parent bound v-model is: {{ model }}
父组件可以用 `v-model` 绑定一个值:
```vue-html [Parent.vue]
<Child v-model="countModel" />
`defineModel()` 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:
- 它的 `.value` 和父组件的 `v-model` 的值同步;
- 当它被子组件变更了,会触发父组件绑定的值一起更新。
这意味着你也可以用 `v-model` 把这个 ref 绑定到一个原生 input 元素上,在提供相同的 `v-model` 用法的同时轻松包装原生 input 元素:
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>
[演练场示例](https://play.vuejs.org/#eNqFUtFKwzAU/ZWYl06YLbK30Q10DFSYigq+5KW0t11mmoQknZPSf/cm3eqEsT0l555zuefmpKV3WsfbBuiUpjY3XDtiwTV6ziSvtTKOLNZcFKQ0qiZRnATkG6JB0BIDJen2kp5iMlfSOlLbisw8P4oeQAhFPpURxVV0zWSa9PNwEgIHtRaZA0SEpOvbeduG5q5LE0Sh2jvZ3tSqADFjFHlGSYJkmhz10zF1FseXvIo3VklcrfX9jOaq1lyAedGOoz1GpyQwnsvQ3fdTqDnTwPhQz9eQf52ob+zO1xh9NWDBbIHRgXOZqcD19PL9GXZ4H0h03whUnyHfwCrReI+97L6RBdo+0gW3j+H9uaw+7HLnQNrDUt6oV3ZBzyhmsjiz+p/dSTwJfUx2+IpD1ic+xz5enwQGXEDJJaw8Gl2I1upMzlc/hEvdOBR6SNKAjqP1J6P/o6XdL11L5h4=)
### 底层机制 {#under-the-hood}
`defineModel` 是一个便利宏。编译器将其展开为以下内容:
- 一个名为 `modelValue` 的 prop,本地 ref 的值与其同步;
- 一个名为 `update:modelValue` 的事件,当本地 ref 的值发生变更时触发。
在 3.4 版本之前,你一般会按照如下的方式来实现上述相同的子组件:
```vue [Child.vue]
然后,父组件中的 `v-model="foo"` 将被编译为:
```vue-html [Parent.vue]
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>
如你所见,这显得冗长得多。然而,这样写有助于理解其底层机制。
因为 `defineModel` 声明了一个 prop,你可以通过给 `defineModel` 传递选项,来声明底层 prop 的选项:
// 使 v-model 必填
const model = defineModel({ required: true })
// 提供一个默认值
const model = defineModel({ default: 0 })
:::warning
如果为 `defineModel` prop 设置了一个 `default` 值且父组件没有为该 prop 提供任何值,会导致父组件与子组件之间不同步。在下面的示例中,父组件的 `myRef` 是 undefined,而子组件的 `model` 是 1:
```vue [Child.vue]
```vue [Parent.vue]
<script setup>
const myRef = ref()
</script>
<template>
<Child v-model="myRef"></Child>
</template>
:::