组件 v-model
跳到导航
跳到搜索
关于
参考:Vue官方文档:《组件 v-model》 有点绕,做个笔记
v-model 用于组件
首先,需要理解 v-model 的等价形式。
在组件上:
<CustomInput v-model="searchText" />
- 会被展开为:
<CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" />
👉相当于:将子组件的属性“modelValue”绑定到响应式状态 searchText,并监听事件“update:modelValue”以新值替换 searchText
- modelValue:展开时,默认的“prop” ——【也是子组件需要定义的 prop】
- 如果子组件中指定了其他 prop,则使用时需要指明 v-model 使用的 prop:
<MyComponent v-model:otherProp="searchText" />
- 如果子组件中指定了其他 prop,则使用时需要指明 v-model 使用的 prop:
- update:modelValue:展开时,默认的“触发事件” ——【也是子组件需要定义的 emit】
把这个“展开形式”理解为类源码实现,子组件只需要根据该形式实现即可。
💡所以,子组件中需要:
- 声明“属性 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 用于组件
对于“存在多个 <input>
的子组件”,需要使用多个 v-model,且需指明 v-model 分别作用与“子组件哪个属性”。
【原理同上一节】
对于:
<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 修饰符
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" />