网站首页 > 博客文章 正文
前言
postMessage Api 想必大家都不陌生,WebWorker 通信会用到,iframe 窗口之间通信也会用到,尤其像一些通过 iframe 嵌入其他项目产品的应用,想要实现实时通信,就少不了它。
但是,监听消息基本都是全局注册事件来接收对应消息,然后在做分发处理的。
假如业务比较复杂,流程比较长,消息通信的频率高,而且通信场景繁琐,那么通过全局事件处理就显得不太合适了。
那么问题来了,我们能不能将 postMessage 进行一次转化,把他变成类似 Promise 的使用方式,这样业务里使用 postMessage 进行通信的时候,就不需要考虑全局回调事件了。而且 Promise 和他的语法糖 await 都可以很好的消除回调地狱的情况。
思考
Promise 化,需要解决那些问题?
- 业务中发起的事件,如何注册并消费?
- 相同的事件多次发起,应该如何处理响应结果?
我们先来看业务中发起的事件,如何注册并消费?
举个实际的例子:我们假定有子系统向父级 iframe 获取 loginToken 的行为。
正常情况我们会这样写:
ts复制代码const messageEventHandler = (event: MessageEvent) => {
const data = event.data;
// 分发事件
emit(data?.api, data);
}
// 接收
window.addEventListener("message", messageEventHandler);
// 发送
window.parent.postMessage({ api: "getLoginToken" }, "*");
这样写逻辑有问题吗?没有问题!逻辑可以正确的执行,事件也成功的被分发了
通过消息订阅来接收通知,单发的时候看起来没什么问题,假如 getLoginToken 被多次触发,或者说,同一个事件被多次注册,那么还需要考虑业务侧事件执行后销毁,避免重复触发
有没有办法可以减轻业务侧的心智负担呢?
办法总比困难多嘛~
我们可以使用一个全局变量 PostMessageCallBackMap 来存放注册的事件及回调,Promise 中的 resolve 正好有阅后即焚的特性,那么我们是不是可以考虑把创建出来的 resolve 作为 callback 放到 Map 中呢?
想到就立即行动!
实现
改造后的代码如下:
ts复制代码export const PostMessageCallBackMap = new Map();
const messageEventHandler = (event: MessageEvent) => {
// 事件分发
const data = event.data;
let eventKey = data?.api;
if (PostMessageCallBackMap.has(eventKey)) {
PostMessageCallBackMap.get(eventKey)(data);
}
};
// 接收
window.addEventListener("message", messageEventHandler);
接收我们写好了,发送这块怎么实现呢?
ts复制代码// 发送
function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> {
return new Promise((resolve, reject) => {
if (window.parent) {
window.parent.postMessage(param, "*");
// 将当前的 resolve 添加到 Map 中,等待返回事件触发
let eventKey = param.api;
PostMessageCallBackMap.set(eventKey, resolve);
}
});
}
// 这样封装一下,业务中使用就十分方便了
const { token } = await sendMessage({ api: "getLoginToken" })
console.log(token);
这样看起来舒服了很多,业务中应用起来也顺手了很多,完结撒花~
等等!
还有个问题呐。
相同的事件多次发起,应该如何处理响应结果?
陷入沉思
emm,按上面的写法,多次发送 getLoginToken 事件,Map 中的 Key 是唯一的,之前的事件会被覆盖掉,再加上异步事件返回时间不确定的话,完蛋了啊!
摆烂!
注释加上,不要调多次!
结束!
测试:哦?是吗?(狂点、狂点、狂点)
好啦好啦,虽然我们可以通过防抖来避免测试疯狂轰炸,但是某些场景下真的会有连续触发 api 的情况,那我们怎么解决呢?
每一个回调给他一个唯一id,然后通过事件id来进行匹配,不管事件执行的时间长短,通过事件id总可以找到另一半。
唯一id,最简单的我们就用时间戳吧。
改造!改造!改造!
ts复制代码export const PostMessageCallBackMap = new Map();
// 发送
function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> {
return new Promise((resolve, reject) => {
if (window.parent) {
param.timestamp = Date.now();
window.parent.postMessage(param, "*");
let eventKey = param.api + param.timestamp;
PostMessageCallBackMap.set(eventKey, resolve);
}
});
}
// 接收
const messageEventHandler = (event: MessageEvent) => {
// 事件分发
const data = event.data;
let eventKey = data?.api;
// timestamp 来保证对应关系
eventKey = data?.api + (data?.timestamp || "");
if (PostMessageCallBackMap.has(eventKey)) {
PostMessageCallBackMap.get(eventKey)(data);
// 由于事件id的存在,Map会越来越大,清除字典防止内存泄漏
setTimeout(() => {
PostMessageCallBackMap.delete(eventKey);
}, 0);
}
};
window.addEventListener("message", messageEventHandler);
总结
通过这样一番分析改造,终于让难以控制的全局注册事件 postMessage 变成了可以链式调用的 Promise,又可以愉快的写业务代码啦~
演示地址:ztstory.github.io/vue-composi…
源码地址:github.com/ZTStory/vue…
作者:ZTStory 链接:https://juejin.cn/post/7345356037404409882 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
猜你喜欢
- 2024-09-26 Vue3全新的前端构建工具vite学习(前端vue项目开发流程及架构)
- 2024-09-26 Vue核心知识:8.5如何在vuex中使用异步修改?推荐行为触发action
- 2024-09-26 Vue.nextTick 的工作机制(vue中nexttick原理)
- 2024-09-26 ES6(四)用Promise封装一下IndexedDB
- 2024-09-26 Vue项目处理错误上报如此简单(vue项目中遇到的问题,你是怎么解决)
- 2024-09-26 Vue开发:通过异步的方式加载vue组件
- 2024-09-26 Vue.js项目模板搭建(vue.js环境搭建)
- 2024-09-26 vue.js深入篇(一、响应式原理)(vue响应式源码解析)
- 2024-09-26 Vue中的nextTick方法是用来做什么的?
- 2024-09-26 Vue - 简单实现一个命令式弹窗组件
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)