【Vue3】21- v-model 在自定义组件标签上的使用

313 阅读1分钟

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 事件是只要输入框内容发生了变化就触发,不用等到失去焦点。