Skip to content

不同模块的数据通信共享如何做的?

  • 场景:这是一个Monorepo+微前端项目,需要在几个子系统和之间共享关键数据。 然后麻烦的点是,这几个子系统的基座系统,不全是同一个,然后,也不全在这个Monorepo当中。在我来这个项目之前,我mt当时是考虑了这个仓库的体积,以及时间成本,就没有全部迁移过来。

总结一下背景就是:有多个基座

好了背景说完了。现在我有这么几种方案:

  • 基于微前端注入的window全局对象:

    • 会污染全局空间,并且没有响应式
    • 任何地方都能修改,难以追溯和调试
    • 在不同基座的系统中,也不好传递
  • 基于 localStorage / sessionStorage

    • 浏览器天然支持,兼容性好
    • 但是
      • 同步阻塞,无响应式,还有容量限制
  • 基于共享的MobX Store、Zustand等等

    • 只需要主应用发布实例,然后通过webpack5的模块邦联模式进行远程共享即可
      • 这里有一个问题?如何确保远程共享的实例是同一个?

      • 答:使用模块邦联的shared,它会确保只要只有一个实例被加载和执行,即使有不同的基座,也可以。

      • 而且模块邦联模式,可以做到与技术栈、代码仓库位置、甚至部署环境都完全解耦。

  • 三级设计:

    • 简单的数据传递: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中卸载。

遇到的问题:

  • 远程加载失败
本站访客数 人次 本站总访问量