vue EventBus重写优化

224 阅读2分钟

初衷

vue3.0取消了全局emit和on,官方推荐使用mitt。由于之前vue2.0全局bus项目出现了问题(页面多个相同组件,其中一个组件卸载调用off事件,其他相同组件失去监听),而且因为我们项目基本off都在组件卸载调用,想要支持组件卸载自动off,所以就想着自己重写。


编码

由于TS项目首先先定义类型

//定义可注册事件类型(根据项目配置)
export type IEventAction = 'PROJECT_MONEY_EVENT';
//定义EventBus函数返回对象
export interface IEventBus {  
    emit: (event: IEventAction, ...args: Array<any>) => void;  
    on: (event: IEventAction, cb: (...args: Array<any>) => void) => void;
}

创建on函数

//存储所有事件map
const eventMap = new Map<IEventAction, Map<number, (...args: Array<any>) => void>>();

export function useEventBus(): IEventBus { 
    //获取当前注册组件的实例用于区别代码复用的组件
    const currentInstance = getCurrentInstance();
    //储存当前组件注册的所有事件 
    const currentEvents: Array<IEventAction> = []; 
    
    function on(event: IEventAction, cb: (...args: Array<any>) => void) { 
        currentEvents.push(event);  
        //根据事件获取是否存在该事件map对象
        const eventUidMap = eventMap.get(event);    
        if (currentInstance) {      
        //有map对象就根据组件id添加函数
            if (eventUidMap) {        
                  eventUidMap.set(currentInstance.uid, cb);
              } else {
                  const eventUidMap = new Map();   
                  if (currentInstance) eventUidMap.set(currentInstance.uid, cb);      
                  eventMap.set(event, eventUidMap);    
             } 
        } 
    }  
} 

创建emit函数

export function useEventBus(): IEventBus { 
    //获取当前注册组件的实例用于区别代码复用的组件
    const currentInstance = getCurrentInstance();
    //储存当前组件注册的所有事件 
    const currentEvents: Array<IEventAction> = []; 
    
    function emit(event: IEventAction, ...args: Array<any>) {  
        //根据发射事件获取全部组件函数并调用
        const eventUidMap = eventMap.get(event);  
        if (eventUidMap) {   
           eventUidMap.forEach((eventAction) => {  
              if (typeof eventAction === 'function') {   
                  eventAction(...args);       
              }     
            });   
         } 
    }  
} 

根据组件卸载自动注销该组件的所有事件

  onBeforeUnmount(() => {  
        if (currentInstance) {   
        //根据当前组件注册的所有事件,清空相同组件id的事件函数
           for (const event of currentEvents) {    
               const eventUidMap = eventMap.get(event);    
               if (eventUidMap) {      
                    eventUidMap.delete(currentInstance.uid);     
                    if (eventUidMap.size === 0) {       
                        eventMap.delete(event);        
                    }      
               }  
            }   
         }  
    }); 

全部代码如下,初步代码后续还在优化,有需要的可以参考。

import { getCurrentInstance, onBeforeUnmount } from 'vue';
//定义可注册事件类型
export type IEventAction = 'PROJECT_MONEY_EVENT';
//定义EventBus函数返回对象
export interface IEventBus {  
    emit: (event: IEventAction, ...args: Array<any>) => void;  
    on: (event: IEventAction, cb: (...args: Array<any>) => void) => void;
}
//存储所有事件map
const eventMap = new Map<IEventAction, Map<number, (...args: Array<any>) => void>>();

export function useEventBus(): IEventBus { 
    //获取当前注册组件的实例用于区别代码复用的组件
    const currentInstance = getCurrentInstance();
    //储存当前组件注册的所有事件 
    const currentEvents: Array<IEventAction> = []; 
    
    function emit(event: IEventAction, ...args: Array<any>) {  
        const eventUidMap = eventMap.get(event);  
        if (eventUidMap) {   
           eventUidMap.forEach((eventAction) => {  
              if (typeof eventAction === 'function') {   
                  eventAction(...args);       
              }     
            });   
         } 
    }  

    function on(event: IEventAction, cb: (...args: Array<any>) => void) { 
        currentEvents.push(event);  
        const eventUidMap = eventMap.get(event);    
        if (currentInstance) {      
            if (eventUidMap) {        
                  eventUidMap.set(currentInstance.uid, cb);
              } else {
                  const eventUidMap = new Map();   
                  if (currentInstance) eventUidMap.set(currentInstance.uid, cb);      
                  eventMap.set(event, eventUidMap);    
             } 
        } 
    }  
    //自动根据组件注销 来注销当前组件的所有事件
    onBeforeUnmount(() => {  
        if (currentInstance) {   
           for (const event of currentEvents) {    
               const eventUidMap = eventMap.get(event);    
               if (eventUidMap) {      
                    eventUidMap.delete(currentInstance.uid);     
                    if (eventUidMap.size === 0) {       
                        eventMap.delete(event);        
                    }      
               }  
            }   
         }  
    }); 
    return { emit, on };
}