vue事件总线(原理、优缺点)

news2025/5/11 12:48:58

目录

  • 一、原理
  • 二、使用方法
  • 三、优缺点
    • 优点
    • 缺点
  • 四、使用注意事项
  • 具体代码
  • 参考:

一、原理

在Vue中,事件总线(Event Bus)是一种可实现任意组件间通信的通信方式。
要实现这个功能必须满足两点要求:
(1)所有组件都能看到它;
(2)可以进行事件的监听;

对于第一个要求,Vue通过内置关系VueComponent.prototype.__proto__ === Vue.prototype,使组件实例对象可以访问到Vue原型上的属性和方法,所以只需把事件总线放在Vue的原型对象上,它就可以被所有组件访问。

至于第二个要求,Vue在原型对象上定义了$on、$emit、$off等方法,用于实现事件监听。基本原理是基于消息订阅发布:调用$on方法时,将函数存在以事件名为key的数组里;当调用$emit时,获取相应数组里的所有函数,并逐个执行。

源码位置:https://github.com/vuejs/vue/blob/main/src/core/instance/events.ts
在这里插入图片描述
在这里插入图片描述
所以事件总线就是一个定义在Vue原型对象上的Vue实例。

new Vue({
  el: '#app',
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this;
  }
})

二、使用方法

一个A组件想给B组件发送数据的场景:
A组件:

// A组件想发送数据,则触发事件并传递参数,参数可以是零个到多个
this.$bus.$emit(事件名, 参数);

B组件:

// B组件想接收数据,则在B组件中给$bus绑定自定义事件,事件的回调留在B组件自身
this.$bus.$on(事件名, 回调函数);
// 需要在beforeDestory钩子中解绑事件,避免内存泄露
this.$bus.$off(事件名, 回调函数);

三、优缺点

优点

  1. 任意组件间通信
  2. 组件解耦
    通过使用事件总线,可以将组件之间的直接依赖关系解耦,使组件更加独立和可复用。组件只需要关注自身的功能,而不用关心其他组件的实现细节。

缺点

  1. 只能被动接收数据,不能随时获取状态
    如果需要随时获取状态,可以使用状态管理工具Vuex或者Pinia。
  2. 代码难以调试、数据流向难以追踪
    在大型应用中,事件总线的滥用可能导致组件之间的关系变得混乱,导致追踪代码执行流程和调试变得更加困难。
  3. 潜在的性能问题
    大量的全局事件监听和触发可能导致性能问题,尤其是在频繁触发事件的情况下。

四、使用注意事项

  1. 避免事件命名冲突
    由于事件总线是一个全局的对象,为避免事件命名冲突导致错误触发/解绑事件,建议使用具唯一性的命名空间前缀区分不同的事件。
  2. 避免错误解绑事件
    在解绑事件时,一定要带上事件名和相应的回调函数,否则可能会错误解绑其他组件的事件,影响其他组件的正常运行。
// 仅将回调函数与事件名解绑
this.$bus.$off(事件名, 回调函数);
// 解绑事件名下的所有事件
this.$bus.$off(事件名);
// 解绑所有事件
this.$bus.$off();
  1. 回调函数不能是匿名函数
    匿名函数会导致事件无法正常被解绑
    在这里插入图片描述
  2. 没有解绑事件,可能导致内存泄露
    当组件被销毁时,相关DOM结点已经从DOM树分离出来了,但是还有绑定的事件指向它,导致这些DOM结点无法被垃圾回收,一直在内存里面,就会引发内存泄露。
    所以在组件销毁时,可以在组件的beforeDestroy钩子中使用$off方法解绑事件,以防止内存泄漏。
    没有解绑时:
    在这里插入图片描述

正常解绑时:
在这里插入图片描述

  1. 注意事件的传参
    在发送事件时,可以通过参数传递数据。但请确保传递的数据是简单且不可变的,避免直接传递引用类型的数据,以免造成数据不一致或意外的修改。
    receive(receiveData) {
      this.receiveData = receiveData;
      // 在接收的组件中修改了对象中的值,发送的组件中的值也会改变
      this.receiveData.text = '111';
    },
  1. 慎用全局事件
    全局事件有很大的便利性,但也容易造成不可预测的问题。在使用事件线时,尽量避免滥用全局事件,可以考虑使用更明确的通信方式。
    (1)props和自定义事件应该是父子间通信的首选;
    (2)兄弟节点通信可通过它们的父节点进行;
    (3)隔代组件通信可以使用provide/inject。它可以避免“prop逐级透传”问题,即prop需要通过许多层级的组件传递下去,但这些组件本身可能并不需要那些prop。
    (4)全局共享的数据管理,一般使用Pinia或Vuex等工具。

具体代码

main.js

import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'
import App from './App.vue'

Vue.config.productionTip = false
Vue.use(ElementUI)

new Vue({
  el: '#app',
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.iBus = this;
  }
})

App.vue(用v-if就可以触发组件销毁,不一定要用tab)

<template>
  <div>
    <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="closeTab">
      <el-tab-pane
          v-for="item in editableTabs"
          :key="item.name"
          :label="item.title"
          :name="item.name"
      >
        <send-tab v-if="item.name === 'sendTab'"/>
        <receive-tab v-else/>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script>

import ReceiveTab from "@/components/ReceiveTab.vue";
import SendTab from "@/components/SendTab.vue";

export default {
  name: 'App',
  components: {SendTab, ReceiveTab},
  data() {
    return {
      editableTabsValue: 'sendTab',
      editableTabs: [
        {
          title: '发送页面',
          name: 'sendTab',
        },
        {
          title: '接收页面',
          name: 'receiveTab',
        },
      ],
      tabIndex: 1,
    }
  },
  methods: {
    closeTab(targetName) {
      let tabs = this.editableTabs;
      let activeName = this.editableTabsValue;
      if (activeName === targetName) {
        tabs.forEach((tab, index) => {
          if (tab.name === targetName) {
            let nextTab = tabs[index + 1] || tabs[index - 1];
            if (nextTab) {
              activeName = nextTab.name;
            }
          }
        });
      }
      this.editableTabsValue = activeName;
      this.editableTabs = tabs.filter(tab => tab.name !== targetName);
    }
  }
}
</script>

SendTab.vue

<script>
export default {
  name: "SendTab",
  data() {
    return {
      input: {
        text: '',
      },
    }
  },
  methods: {
    send() {
      console.log('--------------emit', this.iBus._events)
      this.iBus.$emit('bus-demo', this.input, 'data2');
    },
  },
  beforeDestroy() {
    console.log('--------------发送组件的beforeDestroy')
  },
}
</script>

<template>
  <div>
    <el-input style="width: 250px" v-model="input.text"/>
    <el-button type="primary" @click="send">发送数据</el-button>
  </div>
</template>

ReceiveTab.vue

<script>
export default {
  name: "ReceiveTab",
  data() {
    return {
      receiveData: null,
    }
  },
  created() {
    this.iBus.$on('bus-demo', this.receive);
    // this.iBus.$on('anon-func', () => {
    //   console.log('---------------匿名函数')
    // });
    console.log('--------------on', this.iBus._events)
  },
  beforeDestroy() {
    this.iBus.$off('bus-demo', this.receive);
    // this.iBus.$off('anon-func', () => {
    //   console.log('---------------匿名函数')
    // });
    console.log('--------------接收组件的beforeDestroy')
  },
  methods: {
    receive(receiveData, otherData) {
      this.receiveData = receiveData;
      // this.receiveData.text = '111';
      console.log('---------第二个参数', otherData)
    },
  },
}
</script>

<template>
  <span>接收到的数据:{{ receiveData }}</span>
</template>

参考:

  • Vue3迁移指南-事件总线
  • 尚硅谷Vue教程-84/85事件总线
  • 【Vue知识】$on和$emit的实现原理
  • 详解Vue事件总线的原理与应用:EventBus
  • 一个Vue页面的内存泄露分析

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

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

相关文章

音频入门(一):音频基础知识与分类的基本流程

音频信号和图像信号在做分类时的基本流程类似&#xff0c;区别就在于预处理部分存在不同&#xff1b;本文简单介绍了下音频处理的方法&#xff0c;以及利用深度学习模型分类的基本流程。 目录 一、音频信号简介 1. 什么是音频信号 2. 音频信号长什么样 二、音频的深度学习分…

Redis --- 分布式锁的使用

我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法&#xff0c;但是这样不能让锁跨JVM也就是跨进程去使用&#xff0c;只能适用在单体项目中如下图&#xff1a; 为了解决这种场景&#xff0c;我们就需要用一个锁监视器对全部集群进行监视…

使用shell命令安装virtualbox的虚拟机并导出到vagrant的Box

0. 安装virtualbox and vagrant [rootolx79vagrant ~]# cat /etc/resolv.conf #search 114.114.114.114 nameserver 180.76.76.76-- install VirtualBox yum install oraclelinux-developer-release-* wget https://yum.oracle.com/RPM-GPG-KEY-oracle-ol7 -O /etc/pki/rpm-g…

2025数学建模美赛|赛题翻译|E题

2025数学建模美赛&#xff0c;E题赛题翻译 更多美赛内容持续更新中...

SpringBoot统一数据返回格式 统一异常处理

统一数据返回格式 & 统一异常处理 1. 统一数据返回格式1.1 快速入门1.2 存在问题1.3 案列代码修改1.4 优点 2. 统一异常处理 1. 统一数据返回格式 强制登录案例中,我们共做了两部分⼯作 通过Session来判断⽤⼾是否登录对后端返回数据进⾏封装,告知前端处理的结果 回顾 后…

C语言学习强化

前言 数据的逻辑结构包括&#xff1a; 常见数据结构&#xff1a; 线性结构&#xff1a;数组、链表、队列、栈 树形结构&#xff1a;树、堆 图形结构&#xff1a;图 一、链表 链表是物理位置不连续&#xff0c;逻辑位置连续 链表的特点&#xff1a; 1.链表没有固定的长度…

反馈驱动、上下文学习、多语言检索增强等 | Big Model Weekly 第55期

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 01 A Bayesian Approach to Harnessing the Power of LLMs in Authorship Attribution 传统方法严重依赖手动特征&#xff0c;无法捕捉长距离相关性&#xff0c;限制了其有效性。最近的研究利用预训练语言模型的…

git reset (取消暂存,保留工作区修改)

出现这种情况的背景&#xff1a;我不小心把node_modules文件添加到暂存区了&#xff0c;由于文件过大&#xff0c;导致不能提交&#xff0c;所以我想恢复之前的状态&#xff0c;但又不想把修改的代码恢复为之前的状态&#xff0c;所以使用这个命令可以只恢复暂存区的状态&#…

Coze插件开发之基于已有服务创建并上架到扣子商店

Coze插件开发之基于已有服务创建并上架到扣子商店 在应用开发中&#xff0c;需要调用各种插件&#xff0c;以快速进行开发。但有时需要调用的插件在扣子商店里没有&#xff0c;那怎么办呢&#xff1f; 今天就来带大家快速基于已有服务创建一个新的插件 简单来讲&#xff0c;就是…

Oracle 创建用户和表空间

Oracle 创建用户和表空间 使用sys 账户登录 建立临时表空间 --建立临时表空间 CREATE TEMPORARY TABLESPACE TEMP_POS --创建名为TEMP_POS的临时表空间 TEMPFILE /oracle/oradata/POS/TEMP_POS.DBF -- 临时文件 SIZE 50M -- 其初始大小为50M AUTOEXTEND ON -- 支持…

企业微信开发009_使用WxJava企业微信开发框架_封装第三方应用企业微信开发002_并且实现多企业授权访问---企业微信开发011

继续接上一节来贴代码: 接下来看 config部分的代码,这部分代码,系统启动的时候,就会执行,从而把配置的一些,配置读取出来,创建,针对每个企业微信的,操作service. 首先看yml配置文件中配置部分: 可以先看一下demo中: 提供了一个配置的示例,当然这个是针对 企业内部自建应用 …

机器学习 - 初学者需要弄懂的一些线性代数的概念

一、单位矩阵 在数学中&#xff0c;单位矩阵是一个方阵&#xff0c;其主对角线上的元素全为1&#xff0c;其余元素全为0。单位矩阵在矩阵乘法中起到类似于数字1在数值乘法中的作用&#xff0c;即任何矩阵与单位矩阵相乘&#xff0c;结果仍为原矩阵本身。 单位矩阵的定义&…

【学术会议-第五届机械设计与仿真国际学术会议(MDS 2025) 】前端开发:技术与艺术的完美融合

重要信息 大会官网&#xff1a;www.icmds.net 大会时间&#xff1a;2025年02月28日-03月02日 大会地点&#xff1a;中国-大连 会议简介 2025年第五届机械设计与仿真国际学术会议&#xff08;MDS 2025) 将于2025年02月28-3月02日在中国大连召开。MDS 2025将围绕“机械设计”…

RabbitMQ 分布式高可用

文章目录 前言一、持久化与内存管理1、持久化机制2、内存控制1、命令行2、配置文件 3、内存换页4、磁盘控制 二、集群1、Erlang的分布式特性2、RabbitMQ的节点类型2.1、磁盘节点 (Disk Node)2.2、内存节点 (RAM Node) 3、构建集群3.1 普通集群3.2 镜像队列3.3、高可用实现方案3…

海康工业相机 SDK对接 Hikvision

有C#基础的&#xff0c;可以参考下&#xff0c;直接上代码 BaseResult 来自于Nuget包&#xff0c;搜Rotion可以搜出来 LS.Standard.Data 海康的接口操作&#xff0c;要先引用相应的dll using MvCamCtrl.NET; using PCZD.Commons.Data.CameraModel; using PCZD.Data; using Sys…

MySQL 二进制安装(正式篇)

Author&#xff1a;Arsen Date&#xff1a;2025/01/24 官方参考文档&#xff1a;点击链接跳转 目录 规划下载安装管理FAQ 规划 OSMySQL Server Version备注CentOS 7.9 or Linux - Generic8.0.33(GNU libc) 2.17 下载 二进制包下载地址&#xff1a;https://downloads.mysql.…

K8S部署DevOps自动化运维平台

持续集成&#xff08;CI&#xff09; 持续集成强调开发人员提交了新代码之后&#xff0c;立刻自动的进行构建、&#xff08;单元&#xff09;测试。根据测试结果&#xff0c;我 们可以确定新代码和原有代码能否正确地集成在一起。持续集成过程中很重视自动化测试验证结果&#…

工业相机 SDK 二次开发-Sherlock插件

本文介绍了 sherlock 连接相机时的插件使用。通过本套插件可连接海康的工业相机。 一&#xff0e;环境配置 1. 拷贝动态库 在用户安装 MVS 目录下按照如下路径 Development\ThirdPartyPlatformAdapter 找到目 录为 DalsaSherlock 的文件夹&#xff0c;根据 Sherlock 版本找到…

分布式版本控制系统:Git

1 Git概述 Git官网&#xff1a;https://git-scm.com/ Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。它具有廉价的本地库、方便的暂存区域和多个工作流分支等特性…

C语言编程笔记:文件处理的艺术

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文一、为什么要用文件二、文件的分…