useModuleApps

返回所有微前端子应用的 React 组件,用于可自由控制路由。

使用姿势

import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

函数签名

function useModuleApps(): Record<string, React.FC<any>>

分别返回包裹每个子应用后的 React 组件。

示例

需要先配置微前端子应用信息。

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  runtime: {
    router: true,
    masterApp: {
      apps: [{
        name: 'Table',
        entry: 'http://localhost:8081',
        // activeWhen: '/table'
      }, {
        name: 'Dashboard',
        entry: 'http://localhost:8082'
        // activeWhen: '/dashboard'
      }]
    },
  },
  plugins: [appTools(), garfishPlugin()],
});

这里演示主应用为自控式路由的场景,全部场景参考 体验微前端

App.tsx
function App() {
  const { apps, MApp, Table, Dashboard } = useModuleApps();

  // 使用的不是 MApp 组件,需要使用 createBrowserRouter 来创建路由
  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" element={<AppLayout />}>
        <Route key={'table'} path={'table/*'} element={<Table />} />
        <Route key={'dashboard'} path={'dashboard/*'} element={<Dashboard />} />
      </Route>
    )
  );

  return (
    // 方法一:使用 MApp 自动根据配置的 activeWhen 参数加载子应用(本项目配置在 modern.config.ts 中)
    // <BrowserRouter>
    //   <MApp />
    // </BrowserRouter>

    // 方法二:手动写 Route 组件方式加载子应用,方便于需要鉴权等需要前置操作的场景
    <>
      <RouterProvider router={router} />
    </>
  );
}

defineConfig(App, {
  masterApp: {
    apps: [
      {
        // name 区分大小写,name 提供的是什么 useModuleApps 返回的就是什么
        name: 'Table',
        entry: 'http://127.0.0.1:8081/',
      },
      {
        name: 'Dashboard',
        entry: 'http://localhost:8082',
      },
    ],
  },
});

通过 useModuleApps() 获取到 TableDashboard 子应用组件(名称和配置里的 name 字段对应),之后就可以像使用普通的 React 组件一样去加载子应用。

集中式路由

集中式路由 是将子应用的激活路由集中配置的方式。我们给子应用列表信息添加 activeWhen 字段来启用 集中式路由

src/App.tsx
import { defineConfig } from '@modern-js/runtime';

defineConfig(App, {
  masterApp: {
    apps: [{
      name: 'Table',
      entry: 'http://localhost:8081',
      // activeWhen: '/table'
    }, {
      name: 'Dashboard',
      entry: 'http://localhost:8082'
      // activeWhen: '/dashboard'
    }]
  },
});

然后在主应用中使用 useModuleApp 方法获取 MApp 组件, 并在主应用渲染 MApp

主应用:App.tsx
import { useModuleApp } from '@modern-js/plugin-runtime';

function App() {
  const { MApp } = useModuleApps();

  return (
    <div>
      <MApp />
    </div>
  );
}

defineConfig(App, {
  masterApp: {
    apps: [
      {
        // name 区分大小写,name 提供的是什么 useModuleApps 返回的就是什么
        name: 'Table',
        activeWhen: '/table',
        entry: 'http://127.0.0.1:8081/',
      },
      {
        name: 'Dashboard',
        activeWhen: '/dashboard',
        entry: 'http://localhost:8082',
      },
    ],
  },
});

这样启动应用后,访问 /dashboard 路由,会渲染 Dashboard 子应用,访问 /table 路由,会渲染 Table 子应用。

加载动画

可以通过以下方式,自定义组件加载过程的过渡动画。

App.tsx
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

import { RouterProvider, Route, createBrowserRouter, createRoutesFromElements, BrowserRouter, Link, Outlet } from '@modern-js/runtime/router';

const AppLayout = () => (
  <>
    <div><Link to={'/table'}>加载约定式路由子应用</Link></div>
    <div><Link to={'/dashboard'}>加载自控式路由子应用</Link></div>
    <div><Link to={'/'}>卸载子应用</Link></div>
    <Outlet />
  </>
)

export default () => {
  const { apps } = useModuleApps();

  // 使用的不是 MApp 组件,需要使用 createBrowserRouter 来创建路由
  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" element={<AppLayout />}>
        {apps.map(app => {
          const { Component } = app;
          // 模糊匹配,path 需要写成类似 abc/* 的模式
          return (
            <Route
              key={app.name}
              path={`${app.name.toLowerCase()}/*`}
              element={
              <Component
                loadable={{
                  loading: ({ pastDelay, error }: any) => {
                    if (error) {
                      return <div>error: {error?.message}</div>;
                    } else if (pastDelay) {
                      return <div>loading</div>;
                    } else {
                      return null;
                    }
                  },
                }}
              />
              }
            />
          )
        })}
      </Route>
    )
  );

  return (
    <>
      <RouterProvider router={router} />
    </>
  );
};