【vue3】学习笔记--组件通信方式

news2024/11/15 9:35:06

学习vue3总是绕不开vue2

vue2组件通信方式总结:

1、props:可以实现父子组件,子父组件,甚至兄弟组件通信
2、自定义事件:实现子父组件通信
3、全局事件总线$bus:可以实现任意组件通信
4、pubsub:发布订阅模式实现任意组件通信
5、vuex: 集中式状态管理容器,实现任意组件通信
6、ref:父组件获取子组件实例VC,获取子组件的响应式数据及方法
7、slot:插槽(默认插槽,具名插槽,作用域插槽),实现父子组件通信

vue3组件通信方式总结:

1、props
2、自定义事件
3、全局事件总线
4、v-model
5、useAttrs方法
6、ref与$parent
7、provide与inject
8、pinia组合式API
9、插槽

vue3组件通信方式如下:

1、父子组件通信props

props数据只读,从父组件传递到子组件,子组件内部不可直接更改
子组件需要使用defineProps方法接受父组件传递过来的数据

父组件

<template>
  <div class="box">
    <Child  :msg="msg"></Child>
  </div>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
import { ref } from "vue";
let msg= ref(10000);
</script>

子组件

<template>
  <div class="son">  
       <p>{{msg}}</p>
  </div>
</template>
<script setup lang="ts">
let props = defineProps(['msg']); //数组|对象写法都可以
</script>

2、自定义事件传值

vue框架中事件分为两种:原生的DOM事件和自定义的事件,原始DOM事件可以让用户与网页进行交互,如:click,dbclick,mouseenter,mouseleave等
自定义事件可以实现子组件给父组件传递数据

如果在父组件的组件标签上直接使用原生DOM事件,在vue2中可以通过.native修饰符变为原生DOM事件,在vue3中就是原生事件,相当于给子组件的根节点绑定的事件,子组件中的任意内容都可以触发这个事件。

<Event1 @click="handler2"></Event1>

vue3中没有this,没有组件实例,利用defineEmits方法返回函数触发自定义事件
父组件

<template>
  <div>
   <!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
    <Event2 @xxx="handler3"  @click="handler4"></Event2>
  </div>
</template>
<script setup lang="ts">
//引入子组件
import Event2 from './Event2.vue';
const handler3 = (param1,param2)=>{
    console.log(param1,param2);
}
const handler4 = (param1,param2)=>{
     console.log(param1,param2);
}
</script>

子组件

<template>
  <div class="child">
    <p>我是子组件2</p>
    <button @click="handler">点击我触发自定义事件xxx</button>
    //直接在模板区触发,当在子组件中定义事件名为click,在父组件中即为自定义事件,不再是原生DOM事件
    <button @click="$emit('click','AK47','J20')">点击我触发自定义事件click</button>
  </div>
</template>

<script setup lang="ts">
let $emit = defineEmits(['xxx','click']);
//按钮点击回调
const handler = () => {
  //第一个参数:事件类型 第二个|三个|N参数即为注入数据
    $emit('xxx','东风导弹','航母');
};
</script>

3、兄弟组件传值

vue3框架中,没有vue构造函数,导致没有VM,也没有原型对象,以及组合式API写法setup没有this,可以用插件mitt实现全局事件总线功能。
mitt官网地址

npm install --save mitt

src目录下新建bus文件夹–index.ts文件中

//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;

emit触发事件,on接收参数

子组件1

import $bus from "../../bus";
import { onMounted } from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
  //第一个参数:即为事件类型  第二个参数:即为事件回调
  $bus.on("car", (car) => {
    console.log(car);
  });
});

在这里插入图片描述
子组件2

<template>
  <div class="child2">
     <button @click="handler">赠送车</button>
  </div>
</template>

<script setup lang="ts">
//引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
  $bus.emit('car',{car:"法拉利"});
}
</script>

4、v-model父子组件通信

vue2中不可以进行父子组件通信,vue3中相当于同时做了props和emit的工作
在这里插入图片描述

5、useAttrs方法

useAttrs方法可以获取组件标签身上的属性与事件
如果同时使用props,props的优先级比useAttrs高。props接受了useAttrs方法就获取不到了
useAttrs方法不仅可以拿到组件标签身上的原生事件,自定义事件也可以拿到

父组件,父组件不需要向子组件传值,组件标签上的type,size,icon,原生事件及自定义事件都可以通过useAttrs方法获取到

<template>
  <div>
    <!-- 自定义组件 -->
    <HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" @xxx="handler"></HintButton>
  </div>
</template>

子组件

<template>
  <div :title="title">
  //对象格式:{type:'primary',size:'small',icon:'Edit',title:'编辑按钮'}
     <el-button :="$attrs"></el-button>   
  </div>
</template>
//引入useAttrs方法:获取组件标签身上属性与事件
import {useAttrs} from 'vue';
//此方法执行会返回一个对象
let $attrs = useAttrs();

如果用props接收title

let props =defineProps(['title']);

props接受了useAttrs方法就获取不到了
在这里插入图片描述
原生事件及自定义事件都可以通过useAttrs方法获取到
在这里插入图片描述

6、ref与$parent

ref获取到真实的DOM节点,可以获取到子组件的实例VC
$parent可以在子组件的内部获取到父组件的实例

1、ref
在父组件通过ref获取子组件的实例

<template>
  <div class="box">
    <Son ref="son"></Son>
  </div>
</template>
import {ref} from 'vue';
let money = ref(100000000);
//获取子组件的实例,与组件标签上的ref同名,不然拿不到实例
let son = ref();

拿到子组件的实例后,组件内部数据对外是关闭的,别人不能访问,想让外部访问需要通过defineExpose方法对外暴露
子组件中

import {ref} from 'vue';
//子组件钱数
let money = ref(666);
defineExpose({
  money,

})

暴露之后,就可以在父组件改子组件的数据了
父组件

  <button @click="handler">修改</button>
  //父组件内部按钮点击回调
const handler = ()=>{
   //子组件钱数减去10
   son.value.money-=10;
 
}

2、$parent
在子组件内部,点击事件的参数中传入 $parent,(固定写法)

子组件

<template>
  <div class="dau">  
     <button @click="handler($parent)">点击我</button>
  </div>
</template>
import {ref} from 'vue';
//子组件钱数
let money = ref(999999);
//子组件按钮点击回调
const handler = ($parent)=>{
   money.value+=10000;
   $parent.money-=10000;
}

打印$parent
在这里插入图片描述
没有拿到父组件的数据,需要在父组件也对外暴露一下
父组件

//对外暴露
defineExpose({
   money
})

此时再打印,就有数据了
在这里插入图片描述

7、provide与inject

可以实现隔辈组件的通信,provide(提供),inject(注入)
1、祖先组件:祖先组件给后代组件提供数据

import { ref, provide } from "vue";
let car = ref("法拉利");

provide 方法有两个参数,key以及value

//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("getData", car);

2、后辈组件,子组件/孙子组件都可以使用并且可以修改值

 <button @click="updateCar">更新数据</button>
import {inject} from 'vue';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject('getData');
const updateCar = ()=>{
   car.value  = '自行车';
}

8、pinia选择式API

vue3中pinia是个大菠萝,看着这么可爱的卡通形象,似乎学习pinia的知识也不是那么枯燥了。
Pinia中文文档
在这里插入图片描述
pinia类似于vue2中的vuex
vuex:集中式管理状态容器,可以实现任意组件之间的通信
核心概念:state、mutations、actions、getters、modules
pinia:集中式管理状态容器,可以实现任意组件之间的通信
核心概念:state、actions、getters

1、src目录下新建store文件夹,新建index.ts及modules文件下,目录如下:
在这里插入图片描述
index.ts

//创建大仓库
import { createPinia } from 'pinia';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;

在main.ts中引入并使用

//引入仓库
import store from './store'
app.use(store)

引入成功后,可以看到
在这里插入图片描述
pinia写法:选择式API和组合式API
1、选择式API在modules中定义小的仓库,info

//defineStore 用于定义小仓库,从pinia中获取
//定义info小仓库
import { defineStore } from "pinia";
//defineStore需要传递两个参数,第一个参数:小仓库名字  第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
    //存储数据:state,vuex中state是对象写法,pinia中是函数写法,函数返回的对象才是给组件提供的数据
    state: () => {
        return {
            count: 99,
            arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        }
    },
    actions: {
    //vuex中commit提交,mutation去修改,pinia中没有mutation,直接在action中修改
        //注意:函数没有context上下文对象,pinia中使用this获取state中的数据
        //没有commit、没有mutations去修改数据
        updateNum(a: number, b: number) {
            this.count += a;
        }
    },
    
    getters: {
        total() {
            let result:any = this.arr.reduce((prev: number, next: number) => {
                return prev + next;
            }, 0);
            return result;
        }
    }
});
//对外暴露方法
export default useInfoStore;

在组件中使用,当数据改变的时候,所有使用的组件中的数据都会一同改变

import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();
console.log(infoStore);

在这里插入图片描述
修改数据

  <button @click="updateCount">点击我修改仓库数据</button>
  

```bash
//修改数据方法
const updateCount = () => {
//可以直接这么写:
//infoStore.count++
//用$patch方法用一个新的对象替换原来的对象
infoStore.$dispath({
count:1111
})

  //仓库调用自身的方法去修改仓库的数据,在info.ts的actions中定义这个方法
  infoStore.updateNum(66,77);
};

在这里插入图片描述
2、组合式API,在modules中定义小的仓库,todo

//定义组合式API仓库
import { defineStore } from "pinia";
import { ref, computed,watch} from 'vue';
//创建小仓库
let useTodoStore = defineStore('todo', () => {
    let todos = ref([{ id: 1, title: '吃饭' }, { id: 2, title: '睡觉' }, { id: 3, title: '打豆豆' }]);
    let arr = ref([1,2,3,4,5]);

    const total = computed(() => {
        return arr.value.reduce((prev, next) => {
            return prev + next;
        }, 0)
    })
    //务必要返回一个对象:属性与方法可以提供给组件使用
    return {
        todos,
        arr,
        total,
        updateTodo() {
            todos.value.push({ id: 4, title: '组合式API方法' });
        }
    }
});

export default useTodoStore;

在组件中使用

  <p @click="updateTodo">{{ todoStore.arr }}{{todoStore.total}}</p>
import useInfoStore from "../../store/modules/info";
//获取小仓库对象
let infoStore = useInfoStore();

//引入组合式API函数仓库
import useTodoStore from "../../store/modules/todo";
let todoStore = useTodoStore();

//点击p段落去修改仓库的数据
const updateTodo = () => {
  todoStore.updateTodo();
};

9、插槽

插槽有三种:默认插槽,具名插槽,作用域插槽

默认插槽,在子组件中放置slot,在父组件的子组件标签中添加的任意内容都会显示在插槽位置

1、默认插槽
子组件:

<template>
  <div class="box">
    <h1>我是子组件默认插槽</h1>
    <!-- 默认插槽 -->
    <slot></slot> 
  </div>
</template>

父组件

<template>
  <div class="box">
<Test>
      <div>
        <pre>大江东去浪淘尽,千古分流人物</pre>
      </div>
    </Test>
      </div>
</template>

在这里插入图片描述
2、具名插槽
具名插槽就是有名字的插槽,子组件将坑位留好,父组件根据插槽名对应的添置内容
v-slot 写法等同于#,v-slot=a相当于#a

子组件

<template>
  <div class="box">
    <h1>具名插槽填充数据a</h1>
    <slot name="a"></slot>
    <h1>具名插槽填充数据b</h1>
    <slot name="b"></slot>
    <h1>具名插槽填充数据</h1>
  </div>
</template>

父组件

<template>
  <div>
    <h1>slot</h1>
    <Test1 :todos="todos">
      <template v-slot="{ $row, $index }">
        <p :style="{ color: $row.done ? 'green' : 'red' }">
          {{ $row.title }}--{{ $index }}
        </p>
      </template>
    </Test1>
    <Test>    
      <!-- 具名插槽填充a -->
      <template #a>
        <div>我是填充具名插槽a位置结构</div>
      </template>
      <!-- 具名插槽填充b v-slot指令可以简化为# -->
      <template #b>
        <div>我是填充具名插槽b位置结构</div>
      </template>
    </Test>
  </div>
</template>

在这里插入图片描述
3、作用域插槽
作用域插槽我多花了点时间去理解,似乎和之前vue2的理解有点不一样,也有可能是在使用vue2的时候没有特别关注作用域插槽的实现。

作用域插槽是可以传递数据的插槽,子组件可以将数据回传给父组件。父组件可以决定这些回传的数据以何种结构或外观在子组件内部去展示。

举个例子,当我们封装了一个列表组件并配有一些查询的输入框或下拉框,a调用页面需要:使用者姓名,使用者部门这两个查询条件,b调用页面需要:商品名称,商品编号这两个查询条件,而除了查询条件不同,其他的都一样,我们不可能封装很多个子组件,那就没有复用的意义了。这个时候就可以使用作用域插槽,在父页面的时候将数据传给子组件,子组件通过作用域插槽将数据回传给父组件,父组件的子组件标签内部通过template标签展示,从而实现展示不同的查询条件。

父组件给子组件传递todos数组

    <Test1 :todos="todos">
    </Test1>
import { ref } from "vue";
//todos数据
let todos = ref([
  { id: 1, title: "吃饭", done: true },
  { id: 2, title: "睡觉", done: false },
  { id: 3, title: "打豆豆", done: true },
  { id: 4, title: "打游戏", done: false },
]);

子组件接收并在内部使用

<template>
  <div class="box">
    <ul>
      <li v-for="(item, index) in todos" :key="item.id">     
      </li>
    </ul>
  </div>
</template>
defineProps(["todos"]);

当我们开始在子组件内部使用作用域插槽,对象中是key和value对应,$row 和 $index相当于是我们定义的key,后面跟的是对应的value。

<template>
  <div class="box">
    <ul>
      <li v-for="(item, index) in todos" :key="item.id">
        <slot :$row="item" :$index="index"></slot>
      </li>
    </ul>
  </div>
</template>

在父组件的子组件标签内部使用插槽回传的数据

<Test1 :todos="todos">
      <template v-slot="{ $row, $index }">
        <p :style="{ color: $row.done ? 'green' : 'red' }">
          {{ $row.title }}--{{ $index }}
        </p>
      </template>
    </Test1>

可能会有疑惑,为啥要写的这么复杂,要从父组件传进去,再传出来。如果a调用页面需要的是绿色和红色两种颜色的判断,而b调用页面需要的是紫色和蓝色的判断,甚至是c,d等调用页面需要其他的颜色。甚至是包裹的标签这个需要p标签,那个需要div标签,甚至是其他的外观或者结构需要不同的展示

也许我们习惯的是从父组件传一个变量到子组件,在子组件内部通过v-if,v-else-if来判断不同的条件。不是很复杂的用哪种都可以,复杂的可以考虑作用域插槽,多一种实现方式,也不是非用不可。

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

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

相关文章

目标检测常用的评价指标

目标检测常用的评价指标 1 IoU&#xff08;Intersection over Union&#xff09;2 GIoU&#xff08;Generalized IoU&#xff09;3 DIoU&#xff08;Distance-IoU&#xff09;4 CIoU&#xff08;Complete-IoU&#xff09;5 EIoU&#xff08;Efficient-IoU&#xff09;6 SIoU7 W…

爬虫入门07——requests中携带cookie信息

爬虫入门07——requests中携带cookie信息 对于需要登陆的网站如果不携带cookie是无法获取我们所需内容的就以查看我在CSDN中的订单为例&#xff0c;在登陆后可以查看到订单信息 而当我们使用Python代码发出请求时&#xff0c;是不携带cookie&#xff0c;因此无法拿到订单相关信…

Flink的状态是否支持任务间共享

背景&#xff1a; 在日常编写代码的过程中&#xff0c;我们经常会在方法内部new很多的其他类对象来进行编码工作&#xff0c;那么对于这种情况怎么让new出来的对象是一个我们特意创建出来的一个mock实例&#xff0c;从而让我们能完全控制new出来的对象的所有行为呢&#xff1f…

【雕爷学编程】Arduino动手做(154)---AFMotor电机扩展板模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

数模混合项目:模拟跨数字走线注意事项

数模混合项目中&#xff0c;模拟在数字上走线是常有的事&#xff0c;这里需要注意几个点: 1.模拟电源在数字上走线影响不大&#xff0c;但尽量走top metal和AP层。 2.模拟高频线&#xff0c;尤其是时钟&#xff0c;尽量不要在数字上走线&#xff0c;非要走&#xff0c;最好下…

数据总线学习

为啥要数据总线 使用服务化方式发布&#xff0c;业务端和中间件完全解耦合。一处生产&#xff0c;处处消费设计理念。提供用户可定制的托管化通用消费方案&#xff08;如同步mysql到缓存&#xff0c;同步mysql到es&#xff0c;消费mysql到大数据等托管服务&#xff09; 特性 …

python configparser模块常用方法以及常见报错处理

configparser 是 Python 中一个用于处理配置文件的标准库&#xff0c;可以帮助你生成、读取和修改配置文件的内容 1. 生成配置文件 import configparser# 创建一个配置文件对象 config configparser.ConfigParser()# 添加配置项和值 config[Section1] {key1: value1, key2: …

java 科学计算库 Smile

官网 https://haifengl.github.io/ github https://haifengl.github.io/ 简介 统计机器智能和学习引擎&#xff0c;或者简称 Smile&#xff0c;是一个有前途的现代机器学习系统&#xff0c;在某些方面类似于 Python 的 scikit-learn。它是用 Java 开发的&#xff0c;也提供…

私域账号防范手册

微信为什么要养号吗&#xff1f;为什么会被封&#xff1f;是什么原理&#xff0c;怎么解封&#xff0c;这些你的了解吗&#xff1f; 来看看这篇文章&#xff0c;这些都能给你解答。

向日葵远程命令执行漏洞(CNVD-2022-10270) 漏洞复现

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 漏洞描述02 影响范围03 利用方式05 实战案例06 修复方案 01 漏洞描述 向日葵远程控制是一款面向企业和专业人员的远程pc管理和控制的服务软件。可以在任何有网络的情况下&#xff0c;轻松访问并控制…

Linux命令之nc命令

一、命令简介 nc是netcat的简写&#xff0c;是一个功能强大的网络工具&#xff0c;有着网络界的瑞士军刀美誉。nc命令在linux系统中实际命令是ncat&#xff0c;nc是软连接到ncat。nc命令的主要作用如下&#xff1a; 实现任意TCP/UDP端口的侦听&#xff0c;nc可以作为server以T…

vue3中的包装响应式数据ref、reactive、toRef、toRefs

一、ref Vue 3中拥有一个新的特性叫做ref&#xff0c;它是一个函数&#xff0c;用于包装响应式数据。与Vue 2的data选项不同&#xff0c;ref可以在普通JavaScript中使用&#xff0c;而不仅仅是在Vue组件中。ref可以将普通的JavaScript数据变成响应式的&#xff0c;这意味着当被…

Python接口自动化测试post请求和get请求,获取请求返回值

引言 我们在做python接口自动化测试时&#xff0c;接口的请求方法有get,post等&#xff1b;get和post请求传参&#xff0c;和获取接口响应数据的方法&#xff1b; 请求接口为Post时&#xff0c;传参方法 我们在使用python中requests库做接口测试时&#xff0c;在做post接口测试…

Go语言-数组和切片有什么异同?

slice 的底层数据是数组&#xff0c;slice 是对数组的封装&#xff0c;它描述一个数组的片段。两者都可以通过下标来访问单个元素。 数组是定长的&#xff0c;长度定义好之后&#xff0c;不能再更改。在 Go 中&#xff0c;数组是不常见的&#xff0c;因为其长度是类型的一部分…

2023年第一届证券基金行业先进计算峰会在沪成功召开

2023年7月7日&#xff0c;在中国计算机学会集成电路设计专委会、中国通信学会金融科技发展促进中心、中国电子工业标准化技术协会新一代计算标准工作委员会和证券基金信息技术创新联盟WG1工作组的指导下&#xff0c;由中科驭数主办的2023年第一届证券基金行业先进计算峰会在上海…

以太网电子产品高低温循环测试

参考标准&#xff1a;《GB/T 2423.22-2012 环境试验 第2部分&#xff1a;试验方法 试验N&#xff1a;温度变化》&#xff1a;试验用来确定元器件、设备或其他产品耐受环境温度快速变化的能力。 1、测试影响因素 温度变化试验用来确定一次或连续多次的温度变化对试验样品的影响…

linux报错 /bin/bash^M:解释器错误:没有那个文件或目录

在Linux中运行脚本时&#xff0c;会出现linux报错 /bin/bash^M:解释器错误:没有那个文件或目录。这是因为我们将在Windows下编写的脚本拷贝到Linux环境中运行时会出现运行不了的情况。主要还是Windows的换行符为\r\n,而Linux环境中的换行符号为\n。 解决方法有&#xff1a;  …

Java安全——基于密码的加密

Java安全 基于密码的加密 基于密码加密和SSL加密的区别 密码加密可以数据和密码分离传输SSL只限于在套接字空间传输的数据进行加密 SSL和密码加密 密码加密是指通过算法将原始信息转换成密文&#xff0c;只有知道相应密钥的人才能解密。Java中常用的密码加密算法包括MD5、SHA、…

Buffer Overflow缓冲区溢出和保护措施

缓冲区溢出是指当数据写入某个缓冲区&#xff08;buffer&#xff09;时&#xff0c;超出了为该缓冲区分配的内存空间&#xff0c;从而导致覆盖了相邻内存区域的情况。这种现象可能导致程序崩溃、数据损坏&#xff0c;甚至引发安全漏洞&#xff0c;允许攻击者利用这一漏洞执行恶…

Python程序开发——第十一章 图形用户页面编程

目录 一、图形用户界面与tkinter二、tkinter的根窗口&#xff08;一&#xff09;根窗口的创建&#xff08;二&#xff09;根窗口的基本方法 三、几何布局管理器&#xff08;一&#xff09;pack布局&#xff08;二&#xff09;grid布局&#xff08;三&#xff09;place布局 四、…