Vue3中的常见组件通信之v-model

Vue3中的常见组件通信之v-model

概述

​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。

组件关系 传递方式
父传子 1. props 2. v-model 3. $refs 4. 默认插槽、具名插槽
子传父 1. props 2. 自定义事件 3. v-model 4. $parent 5. 作用域插槽
祖传孙、孙传祖 1. $attrs 2. provide、inject
兄弟间、任意组件间 1. mitt 2. pinia

​ props和自定义事件详见本人另一篇文章:
Vue3中的常见组件通信之props和自定义事件

mitt用法详见本人另一篇文章:
Vue3中的常见组件通信之mitt

下面接着前面的文章继续记录v-model的用法。

4.v-model

v-model常用于普通html标签中的双向绑定,这个绑定用法无法实现跨组件通信,v-model用在组件标签中的时候,可以实现父子间的组件通信,而这样通信方式常用于UI组件库。要理解UI组件库的v-model的双向通信原理,需要先明白普通html标签中的v-model的底层原理。

4.1 普通HTML标签中v-model实现双向绑定的底层原理

在普通html标签中用v-model可以实现数据的双向绑定,如下代码所示是把input输入框里的数据与username进行双向绑定:

vue 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>    
    <br>
    <input id="input1" type="text" v-model="username">
  </div>
</template>

<script setup lang="ts" name="Father">
	import { ref } from "vue"; 
  // 数据
  let username = ref('zhansan')

</script>

<style scoped>
.father {
  height: 300px;
  padding: 20px;
  color: #ffffff;  
  text-align:center; 
  background-image: url(https://xyyhxxx.oss-cn-beijing.aliyuncs.com/picGoImg/202406041301030.png);
  background-size: cover
}
#input1{
  color: #000;
}
</style>

运行后在浏览器打开vue开发者工具,如下图所示:

上图中更改vue开发者工具中的username的值页面也会跟着发生变化,这个实现的是把数据呈现在页面,如果修改input输入框中的内容,username的数据也会跟着发生改变,这个实现的是页面传向数据,这就双向绑定中的双向,而实现这个双向绑定的关键就是在于input标签中写了v-model,如下所示:

vue 复制代码
<input id="input1" type="text" v-model="username">

而上面的代码实现的底层原理是这样的,先把上面的代码改成如下图所示:

vue 复制代码
<input id="input1" type="text" :value="username">

这样可以实现数据呈现在页面,数据修改页面也会跟着修改,但是修改页面,数据却不会变化,这只实现了一个方向的数据绑定,接着再给input标签增加属性,如下代码:

vue 复制代码
<input 
       id="input1" 
       type="text" 
       :value="username" 
       @input="username=$event.target.value"
       >

这样再测试,就会发现页面中的数据也可以传向数据了,修改input标签中的内容,数据也会变化。

注意上面代码中@input="username= e v e n t . t a r g e t . v a l u e " 这句代码 t s 会报警,我们需要处理一下,对 event.target.value"这句代码ts会报警,我们需要处理一下,对 event.target.value"这句代码ts会报警,我们需要处理一下,对event.target进行断言,报警就会消失:

vue 复制代码
<input 
      id="input1" 
      type="text" 
      :value="username" 
      @input="username=(<HTMLInputElement>$event.target).value"
      >

普通input标签中v-model实现双向绑定的底层原理就是:value+@input事件。

4.2组件标签中v-model中实现双向绑定

首先准备一个自己的UI组件,作为子组件,代码如下:

vue 复制代码
<template>
    <input type="text">
</template>

<script setup lang="ts" name="MyInput">

</script>

<style scoped>
    input{
        background-color:transparent;
        color: #ffffff;
        border: 0px;
        border-bottom: 1px solid #ffffff ;
        margin: 5px;
    }
</style>

然后在父组件中引入:

typescript 复制代码
//引入MyInput组件
  import MyInput from "./MyInput.vue";

在父组件中把MyInput组件呈现在页面中:

vue 复制代码
<label>用户名:<MyInput/></label>

运行效果如下:

这样效果出来了,但是没有还没有实现数据绑定,首先在MyInput组件标签上增加:modelValue属性和绑定@update:model-value事件,如下代码:

vue 复制代码
<MyInput :modelValue="username" @update:model-value="username=$event"/>

然后需要在MyInput组件中声明props和声明事件来接收数据和事件:

typescript 复制代码
//接收props
defineProps(["modelValue"])
//声明事件
let emit = defineEmits(['update:model-value'])

最后在MyInput组件中的普通html标签中添加:value属性和绑定@input事件:

vue 复制代码
<input 
       type="text" 
       :value="modelValue"
       @input="emit('update:model-value',(<HTMLInputElement>$event.target).value)"
>

至此,已经实现了父组件和子组件MyInput组件的双向通信,如下图所示:

最后在父组件中的MyInput组件标签上可以直接简写为如下代码:

vue 复制代码
<MyInput v-model="username"/>

实现的效果是完全一样的。

我们在用UI组件库的时候可以直接这样写,前提是UI组件库已经处理好了底层逻辑。

以下是完整代码:

父组件:

vue 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>    
    <br>
    <!-- <input id="input1" type="text" v-model="username"> -->

    <!-- 下面是v-model 的本质 -->
    <!-- <input 
      id="input1" 
      type="text" 
      :value="username" 
      @input="username=(<HTMLInputElement>$event.target).value"
      > -->
    
    <!-- 下面是v-model 的本质 -->
    <!-- <label>用户名:<MyInput :modelValue="username" @update:model-value="username=$event"/></label> -->

    <label>用户名:<MyInput v-model="username"/></label>

  </div>
</template>

<script setup lang="ts" name="Father">
	import { ref } from "vue"; 
  //引入MyInput组件
  import MyInput from "./MyInput.vue";
  // 数据
  let username = ref('zhansan')

</script>

<style scoped>
.father {
  height: 300px;
  padding: 20px;
  color: #ffffff;  
  text-align:center; 
  background-image: url(https://xyyhxxx.oss-cn-beijing.aliyuncs.com/picGoImg/202406041301030.png);
  background-size: cover
}
#input1{
  color: #000;
}
</style>

MyInput组件:

vue 复制代码
<template>
    <input 
        type="text" 
        :value="modelValue"
        @input="emit('update:model-value',(<HTMLInputElement>$event.target).value)"
    >
</template>

<script setup lang="ts" name="MyInput">
    //声明props
    defineProps(["modelValue"])
    //声明事件
    let emit = defineEmits(['update:model-value'])
</script>

<style scoped>
    input{
        background-color:transparent;
        color: #ffffff;
        border: 0px;
        border-bottom: 1px solid #ffffff ;
        margin: 5px;
    }
</style>

4.3一个UI组件实现多个数据传送

在父组件中可以改value,比如改成usName,如下代码所示:

vue 复制代码
<MyInput v-model:usName="username"/>

这个代码的本质是如下代码:

vue 复制代码
<MyInput :usName="username" @update:usName="username=$event"/>

在MyInput组件代码中就需要改成如下代码:

vue 复制代码
<template>
    <input 
        type="text" 
        :value="usName"
        @input="emit('update:usName',(<HTMLInputElement>$event.target).value)"
    >
</template>

<script setup lang="ts" name="MyInput">
    //声明props
    defineProps(["usName"])
    //声明事件
    let emit = defineEmits(['update:usName'])
</script>

这样改完后运行效果跟之前是完全一样的,接下来再扩展一下,父组件中的MyInput标签改成如下代码:

vue 复制代码
<MyInput v-model:usName="username" v-model:paword="password"/>

然后在MyInput组件中代码改成如下:

vue 复制代码
<template>
    <input 
        type="text" 
        :value="usName"
        @input="emit('update:usName',(<HTMLInputElement>$event.target).value)"
    >
    <br>
    <input 
        type="text" 
        :value="paword"
        @input="emit('update:paword',(<HTMLInputElement>$event.target).value)"
    >
</template>

<script setup lang="ts" name="MyInput">
    //声明props
    defineProps(["usName",'paword'])
    //声明事件
    let emit = defineEmits(['update:usName','update:paword'])
</script>

这样就实现一个组件内双向绑定两个数据了,如下图所示:

4.4小结

v-model可以实现父子间的通信,v-model即可以设置在普通html标签中,也可以设置在组件标签中,设置在组件标签中可以实现父子间的双向通信,前提是子组件底层做了处理。

相关推荐
逐·風3 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦4 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果5 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄6 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰7 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
清灵xmf7 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询