【Vue3教程】组件通信

news2024/9/19 11:03:20

组件通信

  • 一、props
  • 二、自定义事件
  • 三、mitt
  • 四、v-model
  • 五、$attrs
  • 六、$refs 和 $parent
  • 七、provide,inject
  • 八、pinia
  • 九、插槽
    • 默认插槽
    • 具名插槽
    • 作用域插槽
  • 总结


一、props

概述:props是使用频率最高的一种通信方式,常用与:父<—>子

  • 若父传子:属性值是非函数
  • 若子传父:属性值是函数
<template>
        <div class="main-div">
                <div class="father-div ">
                        <h2>父组件</h2>
                        <h3>我有一辆{{ car }}</h3>
                        <h3>儿子的玩具{{toy}}</h3>
                        <ProsSon :car="car" :sendToy="getToy"/>
                </div>
        </div>
</template>
<script lang="ts" setup>
import ProsSon from './prosSon.vue'
import {ref} from 'vue'

let car =  ref("宝马")
let toy = ref() 
function getToy(value:string){
      toy.value = value
}
</script>
<style scoped>
.main-div {
        display: flex;
        justify-content: center;
        align-items: center;
}

.father-div {
        width: 600px;
        height: auto;
        background: orange;
        display: flex;
        justify-content: top;
        align-items: center;
        flex-direction: column;
        border: solid gray 1px;
        border-radius: 15px;
}
</style>
<template >
        <div class="son-div">
            <h2>子组件</h2>
            <h3>我有一个:{{toy}}</h3>
            <h3>老爸有一辆{{car}}</h3> 
            <button @click="sendToy(toy)">把玩具给父亲</button>
        </div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
//数据
let toy = ref("奥特曼")
//声明接受props
defineProps(["car","sendToy"])
</script>
<style scoped>
.son-div{
       background-color: aquamarine;
       width: 500px;
       height: 250px;
       margin: 10px;
       border: solid gray 1px;
       border-radius: 15px;
}
</style>

二、自定义事件

概述:自定义事件常用于:子 => 父。
示例:

<template>
        <div class="father">
                <h2>父组件</h2>
                <h3>儿子玩具:{{ toy }}</h3>
                <!-- 在父组件中,给子组件绑定自定义事件 -->
                <Child @send-toy="saveToy"></Child>
        </div>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import {ref} from 'vue'

let toy = ref()

function saveToy(value:string){
       toy.value = value
}
</script>
<style scoped>
.father {
        background: gray;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin: 0 10px;
}
</style>
<template>
        <div class="child">
              <h3>子组件</h3>
              <h4>玩具:{{ toy }}</h4>
              <button @click="$emit('send-toy',toy)">把玩具给父亲</button>
        </div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';

let toy = ref("奥特曼")
defineEmits(['send-toy'])
</script>
<style scoped>
        .child{
                background-color: rgb(76, 209, 76);
                padding: 10px;
                box-shadow: 0 0 10px black;
                border-radius: 10px;
        }
</style>

三、mitt

概述:与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。
安装

npm i mitt

测试一下,在utils/emitter.ts文件中写入如下代码:

//引入mitt
import mitt from 'mitt'
//调用mitt得到emitter,emitter能绑定事件,触发事件
const emitter = mitt()

//绑定事件示例
emitter.on('test1',()=>{
    console.log("test1被调用了")
})

//触发事件示例
emitter.emit('test1')
//解绑事件示例
emitter.off('test1')
//暴露emitter
export default emitter

接收数据的组件中:绑定事件、同时在销毁前解绑事件:

import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";

// 绑定事件
emitter.on('send-toy',(value)=>{
  console.log('send-toy事件被触发',value)
})

onUnmounted(()=>{
  // 解绑事件
  emitter.off('send-toy')
})

【第三步】:提供数据的组件,在合适的时候触发事件

import emitter from "@/utils/emitter";

function sendToy(){
  // 触发事件
  emitter.emit('send-toy',toy.value)
}

注意这个重要的内置关系,总线依赖着这个内置关系

四、v-model

1、概述:实现父与子之间互相通信。
2、前序知识—— v-model的本质

   <!-- 使用v-model指令 -->
   <input type="text" v-model="userName">
   
   <!-- v-model的本质是下面这行代码 -->
   <input 
     type="text" 
     :value="userName" 
     @input="userName =(<HTMLInputElement>$event.target).value"
   >

3、组件标签上的 v-model的本质::moldeValueupdate:modelValue事件。

<SelfInput v-model="userName"/>
<!-- 组件标签上v-model 的本质 -->
<SelfInput :model-value="userName" @update:model-value="userName = $event"></SelfInput>

SelfInput组件中:

<template>
        <div class="box">
           <input type="text" :value="modelValue"  @input="emit('update:model-value',(<HTMLInputElement>$event.target).value)"/>
        </div>
</template>
<script setup lang="ts">
//接受props
defineProps(['modelValue'])
//声明事件
const emit = defineEmits(['update:model-value'])
</script>

4、 也可以更换value,例如改为abc

<!-- 也可以更换value,例如改成abc-->
<SelfInputTemp v-model:abc="password"></SelfInputTemp>
<!-- 上面代码的本质如下 -->
<SelfInputTemp :abc="password" @update:abc="password = $event" />

SelfInputTemp组件中

<template>
     <div class="box">
           <input type="text" :value="abc"  @input="emit('update:abc',(<HTMLInputElement>$event.target).value)"/>
     </div>
</template>
<script setup lang="ts">
defineProps(["abc"])
const emit = defineEmits(['update:abc'])
</script>

5、如果value可以更换,那么就可以在组件上多次使用v-model

<SelfInput v-model:abc='abc' v-model:xyx='xyz' />

五、$attrs

1、概述:$attrs用于实现当前组件的父组件,向当前组件的子组件通过(祖 -> 孙
2、具体说明:$attrs是一个对象,包含所有父组件传入的标签属性

注意:$attrs会自动排除props中声明的属性(可以认为声明过的props被子组件自己消费了)

父组件

<template>
        <div class="father">
                <h2>父组件</h2>
                <Child :a="a" :b="b" :c="c" v-bind="{x:1,y:2}" :updateA="updateA" ></Child>
        </div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
import {ref} from 'vue'
let a = ref(1)
let b = ref(2)
let c = ref(3)

function updateA(value : number){
        a.value = value
}

</script>
<style scoped>
.father {
        background: #ffe8e8;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin: 0 10px;
}
</style>

子组件

<template>
        <div class="child">
                <h2>子组件</h2>
                <GrandChild v-bind="$attrs"/>
        </div>
</template>
<script setup lang="ts">
import GrandChild from './GrandChild.vue';
</script>
<style scoped>
.child {
        background-color: rgb(76, 209, 76);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

孙组件

<template>
        <div class="GrandChild">
                <h3>孙子组件</h3>
                <h4>a:{{ a }}</h4>
                <h4>b:{{ b }}</h4>
                <h4>c:{{ c }}</h4>
                <h4>x:{{ x }}</h4>
                <h4>y:{{ y }}</h4>
                <button @click="updateA(666)">点我更新</button>
        </div>
</template>
<script setup lang="ts">
defineProps(['a','b','c','x','y','updateA'])
</script>
<style scoped>
.GrandChild {
        background-color: rgb(62, 142, 222);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

六、$refs 和 $parent

  1. 概述:

$refs 用于: 父->子.
$parent 用于 子->父。

父组件:

<template>
        <div class="father">
                <h2>父组件</h2>
                <h3>房产:{{ house }}</h3>
                <button @click="changeToy">修改c1玩具</button>
                <button @click="changeComputer">修改c2电脑</button>
                <button @click="sendBooks($refs)">发书</button>
                <Child1 ref="c1" />
                <Child2 ref="c2" />
        </div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child1 from './Child1.vue'
import Child2 from './Child2.vue';
const house = ref(4)
const c1 = ref()
const c2 = ref()

function changeToy() {
        c1.value.toy = "黑神话:悟空"
}

function changeComputer(){
        c2.value.computer = "华为"
}

function sendBooks(refs: { [key: string]: any; }){
     for(let key in refs){
        refs[key].books +=3
     }
}
//向外暴露house
defineExpose({house})
</script>
<style scoped>
.father {
        background: gray;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin: 0 10px;
}
</style>

组件一

<template>
        <div class="child">
                <h2>子组件1</h2>
                <h3>玩具:{{ toy }}</h3>
                <h3>书:{{ books }} 本</h3>
                <button @click="getHosue($parent)">给我一套房</button>
        </div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
let toy = ref('孙悟空')
let books = ref(3)
function getHosue(parent: any){
      parent.house -= 1  
}
defineExpose({toy,books})
</script>
<style scoped>
.child {
        background-color: rgb(76, 209, 76);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

组件二

<template>
        <div class="child">
                <h2>子组件2</h2>
                <h3>电脑:{{ computer }}</h3>
                <h3>书:{{ books }} 本</h3>
        </div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
let computer = ref('华硕')
let books = ref(4)

defineExpose({computer,books})
</script>
<style scoped>
.child {
        background-color: rgb(76, 209, 76);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

七、provide,inject

  1. 概述:实现祖孙组件直接通信
  2. 具体使用
    1. 在祖先组件中通过provide 配置向后代组件提供数据
    2. 在后代组件中通过inject配置来声明接收数据
  3. 具体编码:
    父组件:
<template>
        <div class="father">
                <h2>父组件</h2>
                <h3>银子:{{ money }}</h3>
                <h3>车子:一辆{{ car.brank }}车,价值{{ car.price }}万元</h3>
                <Child></Child>
        </div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
import {ref,reactive,provide} from 'vue'
let money = ref(100)
let car = reactive({
        brank:'奇瑞',
        price:'14'
})
function updateMoney(value:number) {
        money.value -= value
}
//向其后代提供数据
provide('moneyContext',{money,updateMoney})
provide('car',car)
</script>
<style scoped>
.father {
        background: #ffe8e8;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin: 0 10px;
}
</style>

子组件

<template>
        <div class="child">
                <h2>子组件</h2>
                <GrandChild/>
        </div>
</template>
<script setup lang="ts">
import GrandChild from './GrandChild.vue';
</script>
<style scoped>
.child {
        background-color: rgb(76, 209, 76);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

孙组件

<template>
        <div class="GrandChild">
                <h3>孙子组件</h3>
                <h4>银子:{{ money }}</h4>
                <h4>车子:一辆{{ car.brand }}, 价值{{car.price }}万元</h4>
                <button @click="updateMoney(6)">花爷爷的钱</button>
        </div>
</template>
<script setup lang="ts">
import { inject } from 'vue';

let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(_x:number)=>{}})
let car = inject('car',{brand:'未知',price:'0'})
</script>
<style scoped>
.GrandChild {
        background-color: rgb(62, 142, 222);
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
}
</style>

八、pinia

详见另一篇文章

九、插槽

默认插槽

父组件中:
        <Category title="今日热门游戏">
          <ul>
            <li v-for="g in games" :key="g.id">{{ g.name }}</li>
          </ul>
        </Category>
子组件中:
        <template>
          <div class="item">
            <h3>{{ title }}</h3>
            <!-- 默认插槽 -->
            <slot></slot>
          </div>
        </template>

具名插槽

父组件中:
        <Category title="今日热门游戏">
          <template v-slot:s1>
            <ul>
              <li v-for="g in games" :key="g.id">{{ g.name }}</li>
            </ul>
          </template>
          <template #s2>
            <a href="">更多</a>
          </template>
        </Category>
子组件中:
        <template>
          <div class="item">
            <h3>{{ title }}</h3>
            <slot name="s1"></slot>
            <slot name="s2"></slot>
          </div>
        </template>

作用域插槽

  1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(新闻数据在News组件中,但使用数据所遍历出来的结构由App组件决定)
  2. 具体编码:
父组件中:
      <Game v-slot="params">
      <!-- <Game v-slot:default="params"> -->
      <!-- <Game #default="params"> -->
        <ul>
          <li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
        </ul>
      </Game>

子组件中:
      <template>
        <div class="category">
          <h2>今日游戏榜单</h2>
          <slot :games="games" a="哈哈"></slot>
        </div>
      </template>

      <script setup lang="ts" name="Category">
        import {reactive} from 'vue'
        let games = reactive([
          {id:'asgdytsa01',name:'英雄联盟'},
          {id:'asgdytsa02',name:'王者荣耀'},
          {id:'asgdytsa03',name:'红色警戒'},
          {id:'asgdytsa04',name:'斗罗大陆'}
        ])
      </script>

总结

在这里插入图片描述

所有代码已上传Gitee

https://gitee.com/z8023y/vue3--component-communication.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2072993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3 element-plus el-table 多层级表头动态渲染。

效果图: html: <el-table :data"arrlist" border style"width: 100%"><template v-for"(i, index) in currentFieldData" :key"index"><el-table-column :label"i.label" :header-D"i.headerAlign&q…

北京理工大学“源源不断”团队在全国大学生物联网竞赛中获得一等奖

在2024年8月24日结束的全国大学生物联网设计竞赛&#xff08;华为杯&#xff09;全国总决赛中的&#xff0c;北京理工大学的“源源不断”团队获得一等奖。 该团队的两名创始成员&#xff0c;张卓玉和杜智聪同学&#xff0c;曾在信息与电子学院李海老师的《智能物联网应用设计》…

vTable实现多维表格

介绍 vTable是字节开发的一款能用来渲染表格的库&#xff0c;是用canvas渲染&#xff0c;避免了传统用dom组件表格的一些问题&#xff0c;能很快的渲染出上万格子的表格。 接下来我将使用vTable构建类似下面的多维表格&#xff0c;其中quantity、sales等是指标。 使用 官网地址…

TensorRT部署模型入门(pythonC++)

文章目录 1. TensorRT安装1.1 cuda/cudnn以及虚拟环境的创建1.2 根据cuda版本安装相对应版本的tensorRT 2. 模型转换2.1 pth转onnx2.2 onnx转engine 3. TensorRT部署TensorRT推理&#xff08;python API&#xff09;TensorRT推理&#xff08;C API&#xff09; 可能遇到的问题参…

洛谷 P2254 [NOI2005] 瑰丽华尔兹

题目来源于&#xff1a;洛谷 题目本质&#xff1a;动态规划&#xff0c;单调队列 解题思路&#xff1a; f[i][x][y] max(f[i - 1][x’][y]) dist(x,y,x,y); i表示的是第i个时间段结束后&#xff0c;(x,y)这个位置最长的滑行距离。 注意(x,y)与(x,y)必定是在同一列或同一行…

数据结构之排序(一)

目录 一.排序的概念及其运用 1.1排序的概念 1.2 常见的排序算法 1.3排序的用途 二、排序的原理及实现 2.1插入排序 2.1.1基本思想 &#xff1a; 2.1.2排序过程&#xff1a; ​编辑2.1.3代码实现 2.1.4直接插入排序的特性总结&#xff1a; 2.2希尔排序&#xff08;希尔…

【TB作品】PIC16F1719单片机,EEPROM,PFM,读写,PIC16F1718/19

对于PIC16F1719单片机&#xff0c;没有直接的EEPROM&#xff0c;而是使用高耐久度的程序闪存&#xff08;PFM&#xff09;作为非易失性数据存储区域。这个区域特别适合存储那些需要频繁更新的数据。读写这个内存区域需要操作一些特殊功能寄存器&#xff0c;比如用于地址的PMADR…

Python - sqlparse 解析库的基础使用

安装 首先打开命令行&#xff0c;输入&#xff1a; pip install sqlparse这样就显示已经安装好了 使用 创建一个 Python 项目&#xff0c;导入 sqlparse 包&#xff1a; 1. parse sql "select * from table1 where id 1;"# 1. parse # parse方法将 SQL语句 解析…

全网最适合入门的面向对象编程教程:38 Python常用复合数据类型-使用列表实现堆栈、队列和双端队列

全网最适合入门的面向对象编程教程&#xff1a;38 Python 常用复合数据类型-使用列表实现堆栈、队列和双端队列 摘要&#xff1a; 在 Python 中&#xff0c;列表&#xff08;list&#xff09;是一种非常灵活的数据结构&#xff0c;可以用来实现堆栈&#xff08;stack&#xff…

如何使用ssm实现国学文化网站的设计与制作

TOC ssm187国学文化网站的设计与制作jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范…

【Kaggle】练习赛《有毒蘑菇的二分类预测》(上)

前言 本篇文章介绍的是Kaggle月赛《Binary Prediction of Poisonous Mushrooms》&#xff0c;即《有毒蘑菇的二分类预测》。与之前练习赛一样&#xff0c;这声比赛也同样适合初学者&#xff0c;但与之前不同的是&#xff0c;本次比赛的数据集有大量的缺失值&#xff0c;如何处…

没有找到c:\windows\system32\msrd3x43.dll。

打开鸭子串口工具&#xff0c;总会出现这个弹窗&#xff1b; 原因&#xff1a;没有以管理员身份运行 解决办法&#xff1a; 1.不用理会它&#xff0c;对串口工具运行没有任何影响。就算你下载了也没用&#xff0c;依然会有提示。 2.或者鼠标右键&#xff0c;以管理员身份运…

go国内源设置

一、背景 部分网络环境不稳定、丢包或无法连外网&#xff0c;在编译go代码时&#xff0c;需要更新相关依赖&#xff0c;可通过设置go国内源地址来更新。 二、国内可用镜像源 2.1 镜像源一 https://goproxy.cn 2.2 镜像源二 https://goproxy.io 2.3 镜像源三 https://gop…

零基础学习Redis(6) -- string类型命令使用

redis中&#xff0c;不同的数据结构有不同的操作命令。 redis中的string是按照二进制存储的&#xff0c;不会对数据做任何编码转换。 1. set / get 命令 为了方便使用&#xff0c;redis提供了多个版本的get / set命令来操作字符串 1. set set key value [expiration EX sec…

NVIDIA将在Hot Chips 2024会议上展示Blackwell服务器装置

NVIDIA 将在 Hot Chips 2024 上展示其 Blackwell 技术堆栈&#xff0c;并在本周末和下周的主要活动中进行会前演示。对于 NVIDIA 发烧友来说&#xff0c;这是一个激动人心的时刻&#xff0c;他们将深入了解NVIDIA的一些最新技术。然而&#xff0c;Blackwell GPU 的潜在延迟可能…

iptables: Chain Already Exists:完美解决方法

iptables: Chain Already Exists&#xff1a;完美解决方法 &#x1f525; iptables: Chain Already Exists&#xff1a;完美解决方法 &#x1f525;摘要引言正文内容 &#x1f4da;什么是 Chain already exists 错误&#xff1f; &#x1f914;常见原因及解决方法 &#x1f52…

排序算法刷题【leetcode88题目:合并两个有序数组、leetcode21:合并两个有序链表】

一、合并两个有序数组 题目比较简单&#xff0c;使用归并排序里面的同样的操作就可以&#xff0c;代码如下所示 #include <iostream> #include <vector> using namespace std;/* leetcode88题&#xff1a;合并两个有序数组 */ class Solution { public:void merge…

九、前端中的异步方法Promise,Promise详解

文章目录 1.Promise简介什么是promise为什么使用Promisepromise中的状态 2.Promis的用法 1.Promise简介 什么是promise Promise是异步编程的一种解决方案&#xff0c;它的构造函数是同步执行的&#xff0c;then 方法是异步执行的。 为什么使用Promise 在JavaScript的世界中…

入门Java编程的知识点—>数组(day05)

重点掌握数组是什么&#xff1f;为什么要使用&#xff1f;如何进行数组的定义&#xff1f; 数组 数组是用来存储同一类型多个元素的存储结构,数组是引用数据类型. 存储同一类型的多个元素如何理解? 生活中: 衣柜→可以存储多个衣服 | 鞋柜→可以存储多个鞋子 | 橱柜→可以存储…

嵌入式Qt移植之tslib部署到Busybox根文件-思维导图-学习笔记-基于正点原子阿尔法开发板

嵌入式Qt移植之tslib部署到Busybox根文件 烧写Busybox根文件系统到开发板 准备好一个固化系统 以TF卡为例子 TF 卡用读卡器插到 Ubuntu 虚拟机 会出现两个分区 boot分区是存放内核和设备树这些 rootfs分区是存放文件系统的 eMMC、NADA FLASH或者其他方式挂载也可&#xf…