浅谈前端工具库设计--通信类

280 阅读3分钟

前言

前端的工具库,常用的也都比较多,一些数据转换、格式化、存储等,也都比较杂,我们试着进行一个大的归类 `

  • 订阅/取消订阅 EventEmitter 前端消息事件订阅方法
  • 页面通信/卸载 EventEmitter 前端消息事件订阅方法
  • socket连接 SocketEmitter 前端消息事件订阅方法,包含 webSocket 使用

应用场景

对于页面的消息通信大家可能感知不是那么敏感,但是项目上肯定会用,一般的应用场景

  • 不同的业务,如需要接收告警消息,或者通知特定用户一些消息,对实时性要求不高

  • 跨页面通信,一般这种解决方案比较多,本文讲的针对单体页面内跨功能通信更合适,因为夸页面就会牵扯到新开浏览器页签方式,这种页面交互就比较复杂,比如:iframe交互postMessageloccalstorage监听等。

image.png

消息通信

公共方法

系统初始化

if (EDUConfig.openSocket) {
   SocketEmitter.connect(); 
 }

方式一:

页面初始化进行订阅

const { SocketEmitter } = window;

 SocketEmitter.subscribe('task', this.handleTask);
 ...

页面卸载时,取消订阅

 SocketEmitter.unsubscribe('task', this.handleTask);
 ...

方式二:

页面通信-初始化

 const fn = () => featureUpdate();
 SocketEmitter.on('feature_update', fn);
 ...

页面通信-卸载等

 SocketEmitter.off('feature_update', fn)
 ...

参数说明

参数说明类型
eventName事件名称string
listener事件接收回调方法function

业务应用

//使用示例
import { SocketEmitter } from '@basic-utils';;
//发送消息
SocketEmitter.emit('emitterName', data);
//开启订阅接收消息
SocketEmitter.on('emitterName', (data) => {});
//关闭订阅
SocketEmitter.off('emitterName', (data) => {});
//开启webSocket
SocketEmitter.connect('/socket.io', null);

源代码 EventEmitter

/**
 * 自定义事件
 * author daerduo
 */
class EventEmitter {
    constructor() {
        this.events = {};
    }
    getEvents() {
        return this.events;
    }
    once(eventName, listener) {
        return this.on(eventName, listener, 0);
    }
    on(eventName, listener, timer = -1) {
        let listeners = this.getListeners(eventName);
        listeners.push({
            listener,
            timer
        });
    }
    emit(eventName, ...args) {
        return this.trigger(eventName, args);
    }
    remove(eventName) {
        this.events[eventName] && delete this.events[eventName];
    }
    off(eventName, listener) {
        let listeners = this.getListeners(eventName);
        let index = listeners.findIndex(item => item.listener === listener);
        index !== -1 && listeners.splice(index, 1);
    }
    clear(){
        this.events = {};
    }
    trigger(eventName, args) {
        let listeners = this.getListeners(eventName);
        for (let i = 0; i < listeners.length; i++) {
            let listener = listeners[i];
            if (listener) {
                listener.listener.apply(this, args || []);
                listener.timer === 0 && listeners.splice(i, 1);
                listeners.length === 0 && delete this.events[eventName];
                listener.timer !== -1 && listener.timer--;
            }
        }
    }
    getListeners(eventName) {
        return this.events[eventName] || (this.events[eventName] = []);
    }
}

export default EventEmitter

上述的代码可以直接拷贝,项目引用进行使用。

支持基本的通信操作,满足功能内交互通信的要求。

实际应用中,SocketEmitter 继承之 EventEmitter,实现基本功能的和扩展功能。

我们来看看 SocketEmitter的代码封装

源代码 SocketEmitter

import EventEmitter from './eventEmitter';
import logger from '../logger';
import cache from '../cache';
import io from 'socket.io-client';

class SocketEmitter extends EventEmitter {
  constructor() {
    super();
    this.sokectEvents = [];
    this.subActions = {};
  }
  connect(url, options) {
    if (this.sokect && this.sokect.connected) {
      return;
    }
    const token = cache.getCache('token', 'session');
    logger.debug('第一次尝试连接socket');
    this.sokect = io(url, { path: '/socket.io', query: { token }, transports: ['websocket'], ...options });
    this.sokect.on('connect', function () {
      logger.debug('socket连接成功');
    });
    this.sokect.on('disconnect', function () {
      logger.debug('socket断开连接');
    });
  }
  disconnect() {
    this.clear();
    this.sokect && this.sokect.disconnect();
    this.sokect = null;
  }

  /**
   * 抽象socket监听方法
   * @param {*} param0
   */
  subscribe(eventName, listener) {
    this.on(eventName, listener);
    if (this.sokectEvents.includes(eventName) || !this.sokect) {
      return;
    }
    this.sokectEvents.push(eventName);
    this.subActions[eventName] = this.subAction(eventName).bind(this);
    this.sokect && this.sokect.on(eventName, this.subActions[eventName]);
    logger.debug(`socket.io listener event -> ${eventName}`);
  }

  subAction(eventName) {
    return function (data) {
      let json;
      try {
        json = JSON.parse(data);
      } catch (e) {
        json = data;
      }
      this.emit(eventName, json);
    };
  }

  /**
   * 抽象socket监听方法
   * @param {*} param0
   */
  unsubscribe(eventName, listener) {
    this.off(eventName, listener);
    this.sokect && this.sokect.off(eventName, this.subActions[eventName]);
    delete this.subActions[eventName];
    this.sokectEvents = this.sokectEvents.filter((v) => v !== eventName);
    logger.debug(`socket.io unlistener event -> ${eventName}`);
  }
}

const event = new SocketEmitter();

const EDUEvent = (function () {
  if (window._EDU_EVENT_) {
    return window._EDU_EVENT_;
  } else {
    window._EDU_EVENT_ = event;
    return event;
  }
})();

export default EDUEvent;

核心代码解读:

1、提供了基本的类导出和实例化操作,以及挂载全局环境。
2、继承了EventEmitter的基本功能,扩展了web socket需要的功能,连接、断开、发送、订阅、关闭订阅等核心功能
3、代码中用到了socket.io-client插件,浏览器socket连接使用,核心方io() 实例化后,/socket.io可以使用nginx代理方式或者配置方式,转发到实际的地址

Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯、通知与消息推送,实时分析等场景中有较为广泛的应用。

WebSocket 的产生源于 Web 开发中日益增长的实时通信需求,对比基于 http 的轮询方式,它大大节省了网络带宽,同时也降低了服务器的性能消耗; socket.io 支持 websocket、polling 两种数据传输方式以兼容浏览器不支持 WebSocket 场景下的通信需求。

老铁们,系列设计持续更新中:

浅谈应用设计-basic-utils工具库
浅谈前端工具库设计--数据转换
浅谈前端工具库设计--本地存储