前端小技巧: TS实现EventBus自定义事件

关于EventBus事件总线

  • 事件总线,实现 on, once, emit, off
    • on, once 是注册函数,存储起来
    • emit时找到对应的函数,执行
    • off找到对应的函数,从对象中删除
  • 注意
    • 区分on和once
    • on绑定的事件可连续执行,除非off
    • once绑定的函数 emit 一次即删除,也可未执行而被 off
    • 数据结构上标识出 on 和 once

实现方案1

代码实现:使用参数区分 on和once

ts 复制代码
class EventBus {
  /*
  {
    key1: [
      {fn: fn1, isOnce: false},
      {fn: fn2, isOnce: false},
      {fn: fn3, isOnce: true},
    ],
    key2: []
  }
  */
 private events: {
  [key: string]: Array<{fn: Function; isOnce: boolean}>
 }
 constructor() {
  this.events = {};
 }

 on(type: string, fn: Function, isOnce: boolean = false) {
  const events = this.events;
  if (events[type] == null) {
    events[type] = []; // 初始化 key 的 fn 数组
  }
  events[type].push({fn, isOnce});
 },
 // 这里是初步版本
 once_origin(type: string, fn: Function) {
  const events = this.events;
  if (events[type] == null) {
    events[type] = []; // 初始化 key 的 fn 数组
  }
  events[type].push({fn, isOnce: true});
 },
 once(type: string, fn: Function, isOnce: boolean = false) {
  this.on(type, fn, true);
 },
 off(type: string, fn?:Function) {
  if (!fn) {
    // 解绑所有 type 的函数
    this.events[type] = [];
  } else {
    // 解绑单个 fn
    const fnList = this.events[type];
    if (fnList.length) {
      this.events[type] = fnList.filter(item.fn !== fn);
    }
  }
 },
 emit(type: string, ...args: any[]) {
  const fnList = this.events[type];
  if (fnList == null) return;
  // 注意
  this.events[type] = fnList.filter(item => {
    const { fn, isOnce } = item;
    fn(...args);
    // 处理once, 它执行一次就要被过滤掉
    if (!isOnce) return true;
    return false;
  })
 }
}

const e = new EventBus();
function fn1(a: any, b: any) {console.log('fn1', a, b)};
function fn2(a: any, b: any) {console.log('fn2', a, b)};
function fn3(a: any, b: any) {console.log('fn3', a, b)};

e.on('key1', fn1);
e.on('key1', fn2);
e.once('key1', fn3);

e.emit('key1', 10, 20); // 触发 fn1, fn2, fn3
e.off('key1', fn1);
e.emit('key1', 100, 200); // 触发 fn2

实现方案2

代码实现: 拆分保存 on和once

js 复制代码
class EventBus {
  private events: { [key: string]: Array<Function>} // {key1: [fn1, fn2]}
  private onceEvents: {[key: string]: Array<Function>} // 结构同上

  constructor() {
    this.events = {}; // 存储 on
    this.onceEvents = {}; // 存储 once
  }
  // on 触发
  on(type: string, fn: Function) {
    const events = this.events;
    if (events[type] === null) events[type] = [];
    events[type].push(fn);
  },
  once(type: string, fn: Function) {
    const onceEvents = this.onceEvents;
    if (onceEvents[type] === null) onceEvents[type] = [];
    onceEvents[type].push(fn);
  },
  // 解绑事件
  off(type: string, fn: Function) {
    if (!fn) {
      // 解绑所有事件
      this.events[type] = [];
      this.onceEvents[type] = [];
    } else {
      // 解绑单个
      const fnList = this.events[type];
      const onceFnList = this.onceEvents[type];
      if (fnList.length) {
        this.events[type] = fnList.filter(curFn => curFN!== fn);
      }
      if (onceFnList.length) {
        this.onceEvents[type] = onceFnList.filter(curFn => curFN!== fn);
      }
    }
  },
  emit(type: string, ...args: any[]) {
    const fnList = this.events[type];
    const onceFnList = this.onceEvents[type];
    if (fnList.length) {
      fnList.forEach(f => f(...args));
    }
    if (onceFnList.length) {
      onceFnList.forEach(f => f(...args));
      // once 执行一次就删除,这里更简单,代码更简洁
      this.onceEvents[type] = [];
    }
  }
}

// 测试用例同上,此处省略

总结

  • 区分 on, once
  • 合理的数据结构,比算法优化更有效
相关推荐
逐·風3 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫4 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦4 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子5 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享6 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
清灵xmf8 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨8 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL8 小时前
npm入门教程1:npm简介
前端·npm·node.js
小白白一枚1119 小时前
css实现div被图片撑开
前端·css