Bridge 概览

介绍

BridgeModule Federation 提供的工具函数,用于加载应用级别模块。 所谓 “应用级别模块”,即 能像应用一样运行的模块,具备应用的框架渲染能力及路由能力。 通过 Bridge 你可以在不同框架中渲染你的应用,并能保证应用间路由能协同工作。这对于微前端应用将非常有帮助。

工具包

@module-federation/bridge-react

@module-federation/bridge-react 适用于 React 框架类型,目前我们支持 React v16、v17、v18 版本。

在此工具包中我们提供 两个 API:

  • createBridgeComponent

用于创建 React 应用类型模块导出,如果你的应用是 React 类型 且希望作为应用类型模块被另一个 MF 应用加载,那么你应该使用它为你的应用创造符合规范的导出。

  • createRemoteComponent

用于在 React 应用中加载应用类型模块,所加载的模块必须被 createBridgeComponent 所包裹,createRemoteComponent 将自动在你的应用中创建渲染上下文以保证模块能正常工作。

@module-federation/bridge-react 的使用见 Host demoRemote demo

@module-federation/bridge-vue3

@module-federation/bridge-vue3 适用于 Vue 框架类型,目前我们支持 Vue v3 版本。

在此工具包中我们提供 两个 API:

  • createBridgeComponent

用于创建 Vue 应用类型模块导出,如果你的应用是 Vue v3 类型 且希望作为应用类型模块被另一个 MF 应用加载,那么你应该使用它为你的应用创造符合规范的导出。

  • createRemoteComponent

用于在 Vue 应用中加载应用类型模块,所加载的模块必须被 createBridgeComponent 所包裹,createRemoteComponent 将自动在你的应用中创建渲染上下文以保证模块能正常工作。

FAQ

Why Bridge?

Bridge 主要用于解决两个问题:

  • 跨应用框架(React、Vue)加载和渲染
  • 支持加载带路由的模块(路由可以很好的协同工作)

这两个问题都是「微前端框架」里重要的特性

未提供对应框架的 bridge 如何解决?

目前 Module Federation 提供了官方的 bridge 工具包,如果你需要其他框架的 bridge 工具包,可以通过 issue 给我们提供反馈,也可以参考现有的 Bridge 如何实现.

Bridge 的实现非常简单,核心就是基于 DOM 渲染,下面是伪代码:

导出模块

export default function () {
  const rootMap = new Map<any, ReactDOM.Root>();
  return {
    render(info: { dom: HTMLElement; basename?: string; memoryRoute?: { entryPath: string; } }) {
      const root = ReactDOM.createRoot(info.dom);
      rootMap.set(info.dom, root);
      root.render(
        <App />,
      );
    },
    destroy(info: { dom: HTMLElement }) {
      const root = rootMap.get(info.dom);
      root?.unmount();
    },
  }
}

加载模块

const LazyComponent =  React.lazy(async () => {
  const m = await loadRemote('remote1/export-app');
  const providerInfo = m.default;
  return {
    default: () => {
      const rootRef = useRef(null);
      const providerInfoRef = useRef<any>(null);

      useEffect(() => {
        const providerReturn = providerInfo();
        providerInfoRef.current = providerReturn;
        providerReturn.render(renderProps);

        return () => {
          providerInfoRef.current?.destroy({
            dom: renderDom.current,
          });
        };
      }, []);
      return <div ref={rootRef}></div>;
    }
  };
});

function Component () {
  return (<React.Suspense fallback={<div>loading</div>}>
  <LazyComponent />
</React.Suspense>)
}