近来,想实现前端功能之间的通信功能。众所周知,前端跨页面之间通信的方式有很多种。
譬如:localStorage、BroadCast channel、cookie、postMessage等多种。
在浏览器兼容,使用复杂度各方面综合考虑下,我准备采用localStorage监听方案,从而实现功能之间跨页面通信的问题。
一、封装跨页面通信工具
1、需求分析,思路梳理
由于,采用localStorage监听方案。故而,我准备封装一个工具函数,实现操作storage内部值的函数。
对于此工具的封装,我暂时有以下几点要求:
1、可以直接查到所有的存储数据,方便后期对此类数据做处理。基于此要求,我的想法是给所有缓存的key,添加固定的前缀,后期获取值时,可过滤指定的键,即可实现。
2、可以区分通知类型,传递数据。基于此要求,我的想法是缓存值对象,将定义为以下对象,当数据发生变化时,可通过对应属性判断各种类型和取值。
3、由于是对功能进行监听,也存在同一个页面多个功能的情况,故而需要实现同页面的监听回调。但是,有些浏览器对同页面监听是不支持的。所以,我考虑采用“订阅”模式,来做到同页面之间的“发布、订阅”功能。
{
"type": "通知类型",
"data": "通知数据",
"time": "当前时间戳"
}
此次设计,我暂时想到的就是以上两点要求,等后期如有添加别的要求,再战。
2、代码实现
- Storage通信模式
/**
* 跨页面通信工具类
*
* @type {{}}
*/
const PageNotifyUtil = {
// 页面通信主键
PAGE_NOTIFY_KEY : "PAGE_NOTIFY_KEY_1689B68216BB4B3CA3CF2BEA19A8C33B_",
/**
* 获取页面Key
*
* @param pageKey
* @returns {string}
*/
getNotifyKey(notifyKey) {
return PageNotifyUtil.PAGE_NOTIFY_KEY + notifyKey;
},
/**
* 通知页面
*
* @param pageKey
* @param type
* @param data
*/
notify(notifyKey, type, data) {
let cacheValue = {
"type": type,
"data": data,
"time": engineCommon.getTimeStamp()
};
StorageUtil.setValue(this.getNotifyKey(notifyKey), JSON.stringify(cacheValue));
},
}
- 发布订阅模式事件处理中心
/**
* 发布订阅事件
*
* @type {{emitFunc(*=, *=, *=): void, emit(*, ...[*]=): void, events: {}, off(*, *): void, on(*, *=): void}}
*/
const EventEmitter = {
// 事件缓存对象
events: {},
/**
* 订阅
*
* @param eventName
* @param callback
*/
on(eventName, callback) {
(this.events[eventName] || (this.events[eventName] = [])).push(callback);
},
/**
* 取消订阅
*
* @param eventName
* @param callback
*/
off(eventName, callback) {
const eventList = this.events[eventName];
eventList && eventList.length && (this.events[eventName] = eventList.filter((f) => f !== callback));
},
/**
* 通知
*
* @param eventName
* @param arg
*/
emit(eventName, ...args) {
this.events[eventName] && this.events[eventName].forEach(fn => fn.apply(this, args));
},
/**
* 通知功能
*
* @param eventName
* @param type
* @param data
*/
emitFunc(eventName, type, data) {
// Storage 通知
PageNotifyUtil.notify(eventName, type, data);
// 同页面事件通知
EventEmitter.emit(eventName, type, data);
}
}
二、功能引擎类实现监听
1、需求分析,思路梳理
如标题所示,我本篇重点在于实现“功能”之间的通信。故而,我将实现的不是单、多页面之间的通信。我的重点在于实现“功能”通信。
我此前所设计的功能架构,大致由下图所示:
如上图所示,我需要在基础引擎类中实现监听,并且提供回调函数,子功能具体实现回调,即可成功回调值对应的子功能中。
2、代码实现
- 通知键获取
/**
* 获取通知键
*
* @returns {string}
*/
engine.getNotifyKey = function () {
// 拼接规则 功能编码 + 功能类型 + 功能模块编码
let notifyKey = engine.funcCode + "_" + engine.getFuncType();
if (engineCommon.isNotEmpty(engine.configCode)) {
notifyKey += "_" + engine.configCode;
}
return notifyKey;
}
- 功能通知监听
// 监听功能
window.onstorage = function (e) {
// 判断是否为当前功能
if (e.key.indexOf(engine.getNotifyKey()) != -1) {
let data = JSON.parse(e.newValue);
engine.notifyCallBack(data.type, data.data)
}
};
// 同页面事件监听
EventEmitter.on(engine.getNotifyKey(), engine.notifyCallBack);
- 通知回调函数
/**
* 通知回调函数
*
* @param type
* @param data
*/
engine.notifyCallBack = function (type, data) {
console.log(` 收到通知回调:${engine.getNotifyKey()} --> ${type} --> ${data}.`);
// 通知回调函数调用
if (engine.onNotifyCallBack) {
engine.onNotifyCallBack(type, data);
}
}
- 功能销毁回调
/**
* 引擎销毁回调
*/
engine.destroy = function () {
// 删除事件监听
EventEmitter.off(engine.getNotifyKey(), engine.notifyCallBack);
}
三、实现效果测试
1、测试代码编写
由于是测试代码,我的想法是统一写在事件处理调用函数中,这样的话所有的事件绑定都有通知效果。我这边考虑对角色管理功能进行通知测试,它的编码为“Sy_Role_100201_DRIVEN_MANAGE_MODEL”。
接下来,我将实现测试功能:
EventEmitter.emit("Sy_Role_100201_DRIVEN_MANAGE_MODEL", "test", "我是测试通知数据");