1. 原理
v-model 用在自定义组件标签上就是 props 和 emit 的结合,实现了父子之间的双向数据流
2. 用法
2.1 介绍
(1) 默认情况
<child v-model="flag"></child>
相当于
<child v-bind:modelValue="flag" @update:modelValue="fn" ></child>
可以看到,绑定的属性名默认为 modelValue,绑定的自定义事件名默认为 update:modelValue
(2) 非默认情况
根据默认情况,可以推出,非默认情况下(自定义属性名)类似的写法
<child v-model:textVal="text"></child>
相当于
<child v-bind:textVal="text" @update:textVal="fn" ></child>
子组件中,还是用 `defineProps` 接收值,用 `defineEmits` 接收自定义事件
同一子组件标签可以多次使用 v-model
2.2 使用
父组件中
// App.vue
<template>
<h1>我是 父组件 APP.vue</h1>
<button @click="flag = !flag">开关</button>
<div>当前状态:flag = {{ flag }}</div>
<div>当前输入值:text = {{ text }}</div>
<hr />
<!-- 给子组件绑定了两个属性,一个是默认情况的,一个是非默认情况的 -->
<v-model v-model="flag" v-model:textVal.isTrue="text"></v-model>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import VModel from './components/VModel.vue';
// 定义属性,准备传入子组件
const flag = ref<boolean>(true)
const text = ref<string>("")
</script>
<style scoped>
</style>
子组件中
// VModel.vue
<template>
<h2>我是子组件 VModel.vue</h2>
<!-- 父组件传过来的属性,一个绑定在了 div 的 v-if,一个绑定在了 input 的 value -->
<div class="dialog" v-if="modelValue">
<input type="text" @input="input" :value="textVal">
<button @click="close">关闭</button>
</div>
</template>
<script setup lang="ts">
// 接收父组件传进来的两个属性
const props = defineProps<{
modelValue: boolean,
textVal: string,
}>()
// 接收父组件传进来的两个自定义事件
const emit = defineEmits(['update:modelValue', 'update:textVal'])
// 点击关闭,触发自定义事件 'update:modelValue',传回去的值直接赋值给了 modelValue
const close = () => {
emit('update:modelValue', false)
}
// input 框输入事件,触发自定义事件 'update:textVal',传回去的值直接赋值给了 textVal
const input = (e:Event) => {
let inputEl = e.target as HTMLInputElement
emit('update:textVal', inputEl.value)
}
</script>
<style scoped>
.dialog{
width: 500px;
height: 150px;
border: 1px solid #ccc;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 20px;
}
.dialog input {
outline: none;
height: 25px;
font-size: 16px;
}
.dialog button {
width: 100px;
margin: 0 auto;
background-color: rgb(254, 211, 131);
border: 1px solid transparent;
color: white;
cursor: pointer;
padding: 5px;
}
.dialog button:hover{
background-color: orange;
}
</style>
2.3 自定义修饰符
举个例子
① 父组件中,在 v-model 后面加上一个自定义的修饰符,比如 .isTrue
<v-model v-model="flag" v-model:textVal.isTrue="text"></v-model>
② 子组件中,就可以根据修饰符来做对应的操作
// 首先接收一下,语法如下
const props = defineProps<{
modelValue: boolean,
textVal: string,
textValModifiers? : { // 如果是默认情况:textValModifiers 应改为 modelModifiers
isTrue: boolean
}
}>()
// 接收之后,可以用这个修饰符来做一些事,比如根据 true or false 来返回不同的内容
const input = (e:Event) => {
let inputEl = e.target as HTMLInputElement
emit('update:textVal', props?.textValModifiers ? inputEl.value + '有修饰符' : inputEl.value + '无修饰符')
}
小彩蛋:change 事件是失去焦点之后才触发的,可用于单选框,复选框,文件上传等情况。 而 input 事件是只要输入框内容发生了变化就触发,不用等到失去焦点。