本文主要记录下微前端中父子应用如何通信
microApp中是通过发布订阅来实现事件通信的,先说一下用法,
最好的方式是像普通属性一样通过micro-app元素传递数据。但自定义元素无法支持对象类型的属性,只能传递字符串,例如<micro-app data={x: 1}></micro-app>
会转换为 <micro-app data='[object Object]'></micro-app>
,想要以组件化形式进行数据通信必须让元素支持对象类型属性
然后通过发布订阅的方式来 实现通信
// /src/data.js // 发布订阅系统 class EventCenter { // 缓存数据和绑定函数 eventList = new Map() /** * 绑定监听函数 * @param name 事件名称 * @param f 绑定函数 */ on (name, f) { let eventInfo = this.eventList.get(name) // 如果没有缓存,则初始化 if (!eventInfo) { eventInfo = { data: {}, callbacks: new Set(), } // 放入缓存 this.eventList.set(name, eventInfo) } // 记录绑定函数 eventInfo.callbacks.add(f) } // 解除绑定 off (name, f) { const eventInfo = this.eventList.get(name) // eventInfo存在且f为函数则卸载指定函数 if (eventInfo && typeof f === 'function') { eventInfo.callbacks.delete(f) } } // 发送数据 dispatch (name, data) { const eventInfo = this.eventList.get(name) // 当数据不相等时才更新 if (eventInfo && eventInfo.data !== data) { eventInfo.data = data // 遍历执行所有绑定函数 for (const f of eventInfo.callbacks) { f(data) } } } } // 创建发布订阅对象 const eventCenter = new EventCenter()
为了使事件之间的通信变的清晰,需要定义一些约束条件
- 基座应用一次只能向指定的子应用发送数据
- 子应用只能发送数据到基座应用
- 子应用之间的数据通信则通过基座应用进行控制
- 格式化订阅名称,就是给事件名加前缀
// 基座应用的数据通信方法集合 export class EventCenterForBaseApp { /** * 向指定子应用发送数据 * @param appName 子应用名称 * @param data 对象数据 */ setData (appName, data) { eventCenter.dispatch(formatEventName(appName, true), data) } /** * 清空某个应用的监听函数 * @param appName 子应用名称 */ clearDataListener (appName) { eventCenter.off(formatEventName(appName, false)) } } // 子应用的数据通信方法集合 export class EventCenterForMicroApp { constructor (appName) { this.appName = appName } /** * 监听基座应用发送的数据 * @param cb 绑定函数 */ addDataListener (cb) { eventCenter.on(formatEventName(this.appName, true), cb) } /** * 解除监听函数 * @param cb 绑定函数 */ removeDataListener (cb) { if (typeof cb === 'function') { eventCenter.off(formatEventName(this.appName, true), cb) } } /** * 向基座应用发送数据 * @param data 对象数据 */ dispatch (data) { const app = appInstanceMap.get(this.appName) if (app?.container) { // 子应用以自定义事件的形式发送数据 const event = new CustomEvent('datachange', { detail: { data, } }) app.container.dispatchEvent(event) } } /** * 清空当前子应用绑定的所有监听函数 */ clearDataListener () { eventCenter.off(formatEventName(this.appName, true)) } }
现在基本上已经差不多了,剩下一步只需要重写 setAttribute 方法就可以了
// 记录原生方法 const rawSetAttribute = Element.prototype.setAttribute // 重写setAttribute Element.prototype.setAttribute = function setAttribute (key, value) { // 目标为micro-app标签且属性名称为data时进行处理 if (/^micro-app/i.test(this.tagName) && key === 'data') { if (toString.call(value) === '[object Object]') { // 克隆一个新的对象 const cloneValue = {} Object.getOwnPropertyNames(value).forEach((propertyKey) => { // 过滤vue框架注入的数据 if (!(typeof propertyKey === 'string' && propertyKey.indexOf('__') === 0)) { cloneValue[propertyKey] = value[propertyKey] } }) // 发送数据 BaseAppData.setData(this.getAttribute('name'), cloneValue) } } else { rawSetAttribute.call(this, key, value) } }
这样的话,就可以传递对象类型的数据了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2021-07-27 Mongodb入门1