Vue3 组件 v-model
Vue3 组件事件里做了个”受控输入组件”,子组件只负责展示和上报,数据都放在父组件。
可父组件每次要用,都得写 :value 加 @update 两行:
<ValueInput :value="keyword" @update="keyword = $event" />
页面里输入框一多,这种两行写法又长又容易写错,能不能一行搞定?
v-model
Vue3 表单绑定讲过,原生 v-model="x" 是 :value 和 @input 的合体。组件上也能用 v-model,把上面两行合成一行:
<ValueInput v-model="keyword" />
组件上的 v-model 等价于”一个 prop 加一个事件”,Vue 把默认名定死了,prop 叫 modelValue,事件叫 update:modelValue。
展开写就是这样:
<ValueInput v-model="keyword" />
<!-- 等价于 -->
<ValueInput :modelValue="keyword" @update:modelValue="keyword = $event" />
@update:modelValue 看着陌生,其实就是自定义事件,名字按 update 加冒号加 prop 名来。
prop 叫 name,事件就叫 update:name,默认 prop 是 modelValue,事件名自然是 update:modelValue。
父组件这边只剩一行,第一个问题解决,但子组件呢,那边也要跟着改吗?
defineModel
父组件一行搞定了,子组件要配合它,就得接住 modelValue 这个 prop,再把变化用 update:modelValue 报回去,两样都得手动写:
<!-- 子组件 ChildInput.vue -->
<script setup>
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
const onInput = (e) => {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="props.modelValue" @input="onInput" />
</template>
prop、事件、传参,名字一个都不能写错。
为了省事,Vue3.4 起提供 defineModel,它把”接 prop 加发事件”整个封装好,子组件里只剩一个 ref:
<!-- 子组件 ChildInput.vue -->
<script setup>
const model = defineModel() // 一个 ref,自动对接 modelValue 和 update:modelValue
const onChange = (e) => {
model.value = e.target.value // 赋值,就等于向上发 update:modelValue
}
</script>
<template>
<input :value="model" @input="onChange" />
</template>
<!-- 父组件 -->
<script setup>
import { ref } from 'vue'
import ChildInput from './components/ChildInput.vue'
const keyword = ref('')
</script>
<template>
<ChildInput v-model="keyword" />
</template>
defineModel 返回一个 ref,读是当前值,赋值自动触发父组件更新。父子两边都只剩一行,双向绑定打通。
多个 v-model
表单通常不止一个字段,比如姓名加年龄。v-model 默认只对应一个 modelValue,想多挂几个,给它们起不同名字就行:
<!-- 子组件 -->
<script setup>
const name = defineModel('name')
const age = defineModel('age')
</script>
<template>
<input v-model="name" placeholder="名字" />
<input v-model="age" placeholder="年龄" />
</template>
<!-- 父组件 -->
<Child v-model:name="userName" v-model:age="userAge" />
速查卡片
| 场景 | 写法 |
|---|---|
| 单个双向绑定 | const model = defineModel() |
| 带默认值 | defineModel({ default: '默认' }) |
| 绑定多个 | defineModel('name') 加 v-model:name |
| 父组件使用 | <Child v-model="keyword" /> 或 <Child v-model:name="n" /> |
| 本质 | prop modelValue 加事件 update:modelValue |