前言
前端的工具库,常用的也都比较多,一些数据转换、格式化、存储等,也都比较杂,我们试着进行一个大的归类 `
- 订阅/取消订阅
EventEmitter
前端消息事件订阅方法 - 页面通信/卸载
EventEmitter
前端消息事件订阅方法 - socket连接
SocketEmitter
前端消息事件订阅方法,包含 webSocket 使用
应用场景
对于页面的消息通信大家可能感知不是那么敏感,但是项目上肯定会用,一般的应用场景
-
不同的业务,如需要接收告警消息,或者通知特定用户一些消息,对实时性要求不高
-
跨页面通信,一般这种解决方案比较多,本文讲的针对单体页面内跨功能通信更合适,因为夸页面就会牵扯到新开浏览器页签方式,这种页面交互就比较复杂,比如:
iframe交互
、postMessage
、loccalstorage监听
等。
消息通信
公共方法
系统初始化
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 场景下的通信需求。
老铁们,系列设计持续更新中: