组件 v-model
跳到导航
跳到搜索
关于
参考:Vue官方文档:《组件 v-model》 有点绕,做个笔记
组件的 v-model 实现
首先,需要理解 v-model 的等价形式(或者说展开形式)。
与“原生元素”类似[1],将 v-model 应用于组件时:
<CustomInput v-model="searchText" />
- 会被展开为:
<CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" />
👉相当于:将子组件的属性“modelValue”绑定到响应式状态 searchText,并监听事件“update:modelValue”以新值替换 searchText
不需要考虑背后是什么原理,只需要根据该形式,完成子组件即可。
💡所以,子组件中需要:
- 声明“属性 modelValue”,并将
<input>
的“value 属性”绑定到该属性; - 声明“事件 update:modelValue”,并让
<input>
的“input 事件”用“<input>
的新值”触发该事件。
组件的实现(方式一)
依据“展开形式”实现子组件,如下:
<script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
如上:
- 声明“属性 modelValue”和“事件 update:modelValue”;
- 将
<input>
的“value 属性”绑定到“modelValue”; - 监听
<input>
的“input 事件”,以携带“<input>
的新值”(即,$event.target.value
)触发“update:modelValue”。
组件的实现(方式二)
以“v-model”替换该“展开形式”实现子组件,如下:
<script setup> import { computed } from 'vue' const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) const value = computed({ get() { return props.modelValue }, set(value) { emit('update:modelValue', value) } }) </script> <template> <input v-model="value" /> </template>
如上:
- 声明“属性 modelValue”和“事件 update:modelValue”
- 提供计算属性“value”:
- 其 getter 用于:获取“modelValue”;
- 其 setter 用于:用 value 触发“update:modelValue”
- 将
<input>
双向绑定到“value”。
v-model:参数
v-model 的参数,用于指定v-model 绑定于“组件的哪个 prop”
——【是否使用参数,需要根据组件实现来定(组件的 <input> 的 value 绑定于哪个 prop)】
——【v-model 默认绑定于组件的“modelValue”属性,其对应的事件为“update:modelValue”】(如上节)
类似的:
<CustomInput v-model:pram="searchText" /> : 会被展开为: <CustomInput :theSpecialProp="searchText" @update:theSpecialProp="newValue => searchText = newValue" />
多 v-model 绑定
对于“存在多个 <input>
的子组件”,只需要使用多个“带参数的 v-model”,以分别与子组件的 prop 绑定即可。
【两种写法,同上一节】
对于:
<UserName v-model:first-name="first" v-model:last-name="last" />
子组件的实现(方式一):
<script setup> defineProps({ firstName: String, lastName: String }) defineEmits(['update:firstName', 'update:lastName']) </script> <template> <input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" /> <input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" /> </template>
子组件的实现(方式二):
<script setup> import { computed } from 'vue' const props = defineProps({ firstName: String, lastName: String }) const emit = defineEmits(['update:firstName', 'update:lastName']) const computedFirstName = computed({ get() { return props.firstName }, set(computedFirstName) { emit('update:firstName', computedFirstName) } }) const computedLastName = computed({ get() { return props.lastName }, set(computedLastName) { emit('update:lastName', computedLastName) } }) </script> <template> <input type="text" v-model="computedFirstName"/> <input type="text" v-model="computedLastName"/> </template>
v-model:自定义修饰符[2]
v-model 有一些内置的修饰符,例如.trim
,.number
和.lazy
。 但某些时候,需要 v-model 支持自定义的修饰符。
v-model 自定义修饰符:
- “自定义修饰符”可以通过在
defineProps
中定义 prop 来捕获。- 对于默认 prop“modelValue”,定义“修饰符 prop”为“modelModifiers”
- 对于其他 prop,定义“修饰符 prop”为“prop名称 + Modifiers” —— 如:title 的“修饰符 prop”为 titleModifiers
- “自定义修饰符”的效果,可以套在
<input>
的事件处理外完成。
示例一:无参数的 v-model 修饰符
对于以下代码,通过 capitalize 修饰符使输入内容的首字母大写:
<MyComponent v-model.capitalize="myText" />
对应子组件的实现为:
<script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) const emit = defineEmits(['update:modelValue']) function emitValue(e) { let value = e.target.value if (props.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } emit('update:modelValue', value) } </script> <template> <input type="text" :value="modelValue" @input="emitValue" /> </template>
示例二:有参数的 v-model 修饰符
对于以下代码,通过 capitalize 修饰符使输入内容的首字母大写:
<MyComponent v-model:title.capitalize="myText" />
对应子组件的实现为:
<script setup> const props = defineProps({ title: String, titleModifiers: { default: () => ({}) } }) const emit = defineEmits(['update:title']) function emitValue(e) { let value = e.target.value if (props.titleModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } emit('update:title', value) } </script> <template> <input type="text" :value="title" @input="emitValue" /> </template>
参考
- ↑
在“原生元素”上:
<input v-model="searchText" />
- 会被展开为:
<input :value="searchText" @input="searchText = $event.target.value" />
- ↑ 对于“指令及其修饰符”的理解,参考:Vue.js:《自定义指令》