不同模块的数据通信共享如何做的?
- 场景:这是一个Monorepo+微前端项目,需要在几个子系统和之间共享关键数据。 然后麻烦的点是,这几个子系统的基座系统,不全是同一个,然后,也不全在这个Monorepo当中。在我来这个项目之前,我mt当时是考虑了这个仓库的体积,以及时间成本,就没有全部迁移过来。
总结一下背景就是:有多个基座
好了背景说完了。现在我有这么几种方案:
基于微前端注入的window全局对象:
- 会污染全局空间,并且没有响应式
- 任何地方都能修改,难以追溯和调试
- 在不同基座的系统中,也不好传递
基于 localStorage / sessionStorage
- 浏览器天然支持,兼容性好
- 但是
- 同步阻塞,无响应式,还有容量限制
基于共享的MobX Store、Zustand等等
- 只需要主应用发布实例,然后通过webpack5的模块邦联模式进行远程共享即可
这里有一个问题?如何确保远程共享的实例是同一个?
答:使用模块邦联的shared,它会确保只要只有一个实例被加载和执行,即使有不同的基座,也可以。
而且模块邦联模式,可以做到与技术栈、代码仓库位置、甚至部署环境都完全解耦。
- 只需要主应用发布实例,然后通过webpack5的模块邦联模式进行远程共享即可
三级设计:
- 简单的数据传递:Url+SessionStorge方案
- 一次性的动作指令或事件通知:对于应用主题的变更、“工单”状态的传递,流程的流转(通知有更新)等等,用事件总线
- useEventBusSubscription来监听,并且自带监听清除。
- 多处共享的持久化的数据:严格状态的变更,比如用户登录的信息,用户权限列表,以及购物车。
- 四层封装:
- 全局实例一致,只有一个实例
- runInAction来进行异步同步以及computed计算衍生值
- 自动缓存到Session中
- 四层封装:
js
// A lightweight wrapper for localStorage communication
class SharedState {
set(key, value) {
const data = JSON.stringify(value);
localStorage.setItem(key, data);
// Manually dispatch an event for same-tab communication
window.dispatchEvent(new CustomEvent('shared-state-change', {
detail: { key, value }
}));
}
get(key) {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
}
// Allow other apps to subscribe to changes
subscribe(key, callback) {
const handler = (event) => {
if (event.detail && event.detail.key === key) {
callback(event.detail.value);
}
};
window.addEventListener('shared-state-change', handler);
// Return an unsubscribe function
return () => window.removeEventListener('shared-state-change', handler);
}
}
// Export a singleton instance
export const sharedState = new SharedState();
即使在业务上落地比较复杂,要考虑很多东西,描述起来也比较简单,前端的工作往往是这样。所以我后面的讲述尽量放在一个具体的场景里面,来显示我的一些方案选择。
模块邦联模式共享实例
js
// shared-module/webpack.config.js
// ...
exposes: {
'./eventBus': './src/eventBus.js',
'./authStore': './src/authStore.js', // 暴露 store
'./UserInfo': './src/UserInfo.jsx', // 暴露组件
},
shared: { // 关键:共享 React 实例,避免多个 React 版本冲突
react: { singleton: true, requiredVersion: deps.react },
// singleton: true,设置了这个才能确保使用的是同一个Store实例
'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
},
// ...
追问点:
- “如果多个应用监听同一个事件,你怎么保证它们的执行顺序?”
- 事件总线模式不引来事件执行顺序。
- 你提到了事件命名规范
- [AppName]:[EventName]:[Action] ,例如 Products:AddToCart:Success
- 如果一个子应用崩溃了,它监听的事件还在吗?会不会导致内存泄漏?
- 要在useEffect中卸载。
遇到的问题:
- 远程加载失败