v-model
Vue2中的v-model
v-model即可以作用于表单元素,又可作用于自定义组件,无论是哪一种情况,它都是一个语法糖,最终会生成一个属性和一个事件
-
当其作用于表单元素时,vue会根据作用的表单元素类型而生成合适的属性和事件。例如,作用于普通文本框的时候,它会生成value属性和input事件,而当其作用于单选框或多选框时,它会生成checked属性和change事件。
-
当其作用于自定义组件时,默认情况下,它会生成一个value属性和input事件。
<Comp v-model="data" />
<!-- 等效于 -->
<Comp :value="data" @input="data=$event" />
可以通过组件的model
配置来改变生成的属性和事件
// Comp
const Comp = {
model: {
prop: "number", // 默认为 value
event: "change" // 默认为 input
}
// ...
}
Comp v-model="data" />
<!-- 等效于 -->
<Comp :number="data" @change="data=$event" />
Vue2比较让人诟病的一点就是提供了两种双向绑定:v-model和.sync
<!-- vue2 -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<!-- 简写为 -->
<ChildComponent :title.sync="pageTitle" />
Vue3的变化
为了让v-model更好的针对多个属性进行双向绑定,Vue3作出了以下修改。
- 当对自定义组件使用v-model指令时,绑定的属性名由原来的value变为modelValue,事件名由原来的input变为update:modelValue。
<!-- vue2 -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
<!-- 简写为 -->
<ChildComponent v-model="pageTitle" />
<!-- vue3 -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
<!-- 简写为 -->
<ChildComponent v-model="pageTitle" />
- 去掉了.sync修饰符,它原本的功能由v-model的参数替代
<!-- vue2 -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<!-- 简写为 -->
<ChildComponent :title.sync="pageTitle" />
<!-- vue3 -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<!-- 简写为 -->
<ChildComponent v-model:title="pageTitle" />
-
model配置被移除
-
允许自定义v-model修饰符
组件内部使用使用props的modelModifiers属性接受自定义修饰符
<template>
<div>
<custom-input v-model.hello.word="message" v-model:msg.sss="message" />
<p>Message: {{ message }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';
const message = ref('ssssssss');
</script>
<!-- CustomInput -->
<template>
<input :value="modelValue" @input="emit('update:modelValue', $event.target.value)">
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const { modelValue, modelModifiers, msg } = defineProps(['modelValue', 'modelModifiers', 'msg']);
console.log(modelValue,msg,modelModifiers); // ssssssss ssssssss {hello: true, word: true}
const emit = defineEmits(['update:modelValue']);
</script>
使用computed拦截v-model
<template>
<div>
<custom-input v-model="params" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';
const params = ref({
name: '张三',
age: 18,
gender: '男',
});
</script>
<template>
<form>
<input v-model="modelRef.name"/>
<input v-model="modelRef.age" />
<input v-model="modelRef.gender" />
</form>
</template>
<script setup>
import {defineEmits,defineProps, computed } from 'vue';
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
// 使用computed拦截v-model
function useVmodel(props, prop, emit) {
return computed({
get() {
return new Proxy(props[prop], {
get(target, key) {
// console.log('get', key, target)
return Reflect.get(target, key)
},
set(target, key, value) {
// console.log('set', key, value)
emit('update:' + prop, {
...target,
[key]: value
})
return true
}
})
},
set(value) {
emit('update:'+prop, value)
}
})
}
const modelRef = useVmodel(props, 'modelValue', emit)
</script>
<style>
input {
border: 1px solid #000;
margin: 20px;
}
</style>