基于 @umi/max 的 Qiankun 微前端开发指南

730 阅读9分钟

在现代前端开发中,微前端架构已经成为一种强大的模式,允许我们将大型的前端应用拆分成多个可独立开发、部署和维护的小型应用。@umi/max 框架内置了 Qiankun 微前端插件,为我们提供了一个便捷的途径来构建微前端系统,使得我们可以轻松地将 Qiankun 微应用集成到 Umi 项目中,从而创建出生产可用的微前端架构。

一、微前端与 Qiankun 简介

微前端将父应用和子应用视为独立的前端项目,父应用可以引入子应用,子应用甚至可以进一步引入孙子应用,形成一个层次化的架构。这种架构有助于不同团队独立开发不同的应用部分,同时又能在最终用户界面中实现无缝集成。Qiankun 是一款流行的微前端框架,为微前端开发提供了强大的功能和良好的开发体验。

二、开始使用

本教程假定您已经对微前端和 Qiankun 微应用的概念以及使用方法有了基本的了解。

三、配置父应用

注册子应用

在父应用中,我们可以通过两种主要方式注册子应用:插件注册和运行时注册。

插件注册子应用

修改父应用的 Umi 配置文件(.umirc.ts),添加以下内容:

export default {
  qiankun: {
    master: {
      apps: [
        {
          name: 'app1',
          entry: '//localhost:7001',
        },
        {
          name: 'app2',
          entry: '//localhost:700',
        },
      ],
    },
  },
};

这里,name 是子应用的名称,用于在引入子应用时进行标识;entry 是子应用运行的 HTTP 地址。您可以查看 此处 获取 master 对象的完整 API。

运行时注册子应用

在 .umirc.ts 中添加:

export default {
  qiankun: {
    master: {},
  },
};

并在父应用的 src/app.ts 文件中导出 qiankun 对象:

export const qiankun = {
  apps: [
    {
      name: 'app1',
      entry: '//localhost:7001',
    },
    {
      name: 'app2',
      entry: '//localhost:7002',
    },
  ],
};

四、配置子应用

子应用需要导出必要的生命周期钩子,以供父应用在不同阶段调用。假设子应用基于 Umi 开发且已引入 qiankun 插件,如果未引入,请按照相关教程配置。

在子应用的 Umi 配置文件(.umirc.ts)中添加:

export default {
  qiankun: {
    slave: {},
  },
};

通过这种方式,微前端插件会自动创建 Qiankun 子应用所需的生命周期钩子和方法,大大简化开发流程。 另外,如果子应用也是使用Umi max 构建的,可以通过配置layout(top,mix等)属性使得默认菜单不重叠。

五、引入子应用

路由绑定引入子应用

适用于子应用包含完整的路由切换逻辑且父子应用路由相互关联的情况。我们可以手动配置 .umirc.ts 文件中的 routes 项,将子应用与路由绑定。例如,要在 /app1/project 和 /app2 路由分别加载子应用 app1 和 app2,可以这样配置父应用的路由:

export default {
  routes: [
    {
      path: '/',
      component: '@/layouts/index.tsx',
      routes: [
        {
          path: '/app1',
          component: '@/layouts/app-layout.tsx',
          routes: [
            // 带上 * 通配符将 /app1/project 下所有子路由都关联给微应用 app1
            {
              path: '/project/*',
              microApp: 'app1',
            },
          ],
        },
        // 配置 app2 关联的路由
        {
          path: '/app2/*',
          microApp: 'app2',
        },
      ],
    },
  ],
};

在此配置中,子应用的路由 base 会在运行时被设置为主应用中配置的 path。例如,若 app1 有一个 /user 路由,在父应用中访问 /app1/project/user 才能正确匹配,否则子应用可能渲染空白或 404 页面。

组件引入子应用

当子应用包含完整的路由切换逻辑且父子应用路由相互关联时,可使用 <MicroApp /> 组件加载子应用。例如:

import { MicroApp } from 'umi';

export default function Page() {
  return <MicroApp name="app1" />;
}

如果父应用的路由包含前缀,可通过 base 属性确保路由正确对应:

import { MicroApp } from 'umi';

export default function Page() {
  return <MicroApp name="app1" base="/prefix/router-path" />;
}

组件引入子应用

当仅使用子应用的指定路由且父子应用路由相互独立时,可使用 <MicroAppWithMemoHistory /> 组件。此组件是 <MicroApp /> 的变体,需要显式提供 url 属性作为子应用的路由:

import { MicroAppWithMemoHistory } from 'umi';

export default function Page() {
  return <MicroAppWithMemoHistory name="app2" url="/some/page" />;
}

六、子应用之间的跳转

如果子应用通过路由绑定引入,可以使用 <MicroAppLink /> 在不同子应用之间跳转。例如:

// 在 app1 中
import { MicroAppLink } from 'umi';

export default function Page() {
  return (
    <>
      {/* 跳转链接为 /app2/home */}
      <MicroAppLink name="app2" to="/home">
        <Button>go to app2</Button>
      </MicroAppLink>
    </>
  );
}

也可以从子应用跳转到父应用的指定路由:

// 在子应用中
import { MicroAppLink } from 'umi';

export default function Page() {
  return (
    <>
      {/* 跳转链接为 /table */}
      <MicroAppLink isMaster to="/table">
        <Button>go to master app</Button>
      </MicroAppLink>
    </>
  );
}

七、子应用生命周期

Qiankun 在 single-spa 的基础上扩展了更多的生命周期钩子,完整的生命周期钩子列表如下:

  • beforeLoad:微应用开始获取前调用,初始状态为 NOT_LOADED
  • load:微应用获取完成时调用,开始获取时状态变为 LOADING_SOURCE_CODE,获取成功为 NOT_BOOTSTRAPPED,失败为 LOAD_ERROR
  • bootstrap:微应用初始化完成时调用,开始初始化时状态为 BOOTSTRAPPING,完成后为 NOT_MOUNTED
  • beforeMount:微应用每次开始挂载前调用。
  • mount:微应用每次开始挂载时调用,状态变为 MOUNTING,挂载完成后为 MOUNTED
  • afterMount:微应用每次挂载完成时调用。
  • beforeUnmount:微应用每次开始卸载前调用。
  • unmount:微应用每次开始卸载时调用,状态变为 UNMOUNTING
  • afterUnmount:微应用每次卸载完成时调用,状态变回 NOT_MOUNTED
  • unload:微应用卸载完成时调用,状态变为 NOT_LOADED
  • update:仅在使用 <MicroApp /> 或 <MicroAppWithMemoHistory /> 组件引入微应用时生效,状态为 MOUNTED 的微应用手动刷新时调用,更新时状态为 UPDATING,更新完成后为 MOUNTED

父应用配置生命周期钩子

在父应用的 src/app.ts 中导出 qiankun 对象进行全局配置:

export const qiankun = {
  lifeCycles: {
    // 所有子应用在挂载完成时,打印 props 信息
    async afterMount(props) {
      console.log(props);
    },
  },
};

子应用配置生命周期钩子

在子应用的 src/app.ts 中导出 qiankun 对象,子应用运行时仅支持配置 bootstrapmount 和 unmount 钩子:

export const qiankun = {
  // 应用加载之前
  async bootstrap(props) {
    console.log('app1 bootstrap', props);
  },
  // 应用 render 之前触发
  async mount(props) {
    console.log('app1 mount', props);
  },
  // 应用卸载之后触发
  async unmount(props) {
    console.log('app1 unmount', props);
  },
};

八、父子应用通信

基于 useModel () 的通信

此为 Umi 推荐的通信方式,基于内置的数据流插件。

主应用透传数据

  • 通过路由模式引入子应用:在父应用的 src/app.ts 里导出 useQiankunStateForSlave() 函数:
export function useQiankunStateForSlave() {
  const [globalState, setGlobalState] = useState<any>({
    slogan: 'Hello MicroFrontend',
  });

  return {
    globalState,
    setGlobalState,
  };
}
  • 通过组件模式引入子应用:直接将数据以组件参数的形式传递给子应用:
import { useState } from 'react';
import { MicroApp } from 'umi';

export default function Page() {
  const [globalState, setGlobalState] = useState<any>({
    slogan: 'Hello MicroFrontend',
  });

  return (
    <MicroApp
      name="app1"
      globalState={globalState}
      setGlobalState={setGlobalState}
    />
  );
}

子应用消费数据
子应用会自动生成一个全局的 Model,其命名空间为 @@qiankunStateFromMaster。可以使用 useModel() 方法获取并消费父应用透传的数据:

import { useModel } from 'umi';

export default function Page() {
  const masterProps = useModel('@@qiankunStateFromMaster');
  return <div>{JSON.stringify(masterProps)}</div>;
}

或者通过高阶方法 connectMaster()

import { connectMaster } from 'umi';

function MyPage(props) {
  return <div>{JSON.stringify(props)}</div>;
}

export default connectMaster(MyPage);

基于配置的通信

在配置父应用注册子应用时,通过 props 属性传递数据给子应用。例如:

export const qiankun = {
  apps: [
    {
      name: 'app1',
      entry: '//localhost:7001',
      props: {
        accountOnClick: (event) => console.log(event),
        accountName: 'Alex',
        accountAge: 21,
      },
    },
  ],
};

子应用可在生命周期钩子中获取并消费这些 props

九、自定义子应用

子应用加载动画

启用此能力后,当子应用正在加载时会显示加载动画,挂载完成后显示内容。

基于 antd 的加载动画

使用 antd 作为项目组件库时,可向子应用传入 autoSetLoading 属性开启加载动画:

  • 通过路由模式引入子应用
export default {
  routes: [
    {
      path: '/app1',
      microApp: 'app1',
      microAppProps: {
        autoSetLoading: true,
      },
    },
  ],
};
  • 通过组件模式引入子应用
import { MicroApp } from 'umi';

export default function Page() {
  return <MicroApp name="app1" autoSetLoading />;
}

自定义加载动画

若不使用 antd 或想自定义加载动画,可设置 loader 作为子应用的加载组件:

  • 通过路由模式引入的子应用(运行时配置)
//.app.tsx
import CustomLoader from 'src/components/CustomLoader';

export const qiankun = () => ({
  routes: [
    {
      path: '/app1',
      microApp: 'app1',
      microAppProps: {
        loader: (loading) => <CustomLoader loading={loading} />,
      },
    },
  ],
});
  • 通过组件模式引入子应用
import CustomLoader from '@/components/CustomLoader';
import { MicroApp } from 'umi';

export default function Page() {
  return (
    <MicroApp
      name="app1"
      loader={(loading) => <CustomLoader loading={loading} />}
    />
  );
}

子应用错误捕获

启用此能力后,当子应用加载异常时会显示错误信息。

基于 antd 的错误捕获组件

使用 antd 作为项目组件库时,可传入 autoCaptureError 属性开启错误捕获能力:

  • 通过路由模式引入子应用
export default {
  routes: [
    {
      path: '/app1',
      microApp: 'app1',
      microAppProps: {
        autoCaptureError: true,
      },
    },
  ],
};
  • 通过组件模式引入子应用
import { MicroApp } from 'umi';

export default function Page() {
  return <MicroApp name="app1" autoCaptureError />;
}

自定义错误捕获组件

若不使用 antd 或想自定义错误捕获组件,可设置 errorBoundary 作为错误捕获组件:

  • 通过路由模式引入的子应用(运行时配置)
//.app.tsx
import CustomErrorBoundary from '@/components/CustomErrorBoundary';

export const qiankun = () => ({
  routes: [
    {
      path: '/app1',
      microApp: 'app1',
      microAppProps: {
        errorBoundary: (error) => <CustomErrorBoundary error={error} />,
      },
    },
  ],
};
  • 通过组件模式引入子应用
import CustomErrorBoundary from '@/components/CustomErrorBoundary';
import { MicroApp } from 'umi';

export default function Page() {
  return (
    <MicroApp
      name="app1"
      errorBoundary={(error) => <CustomErrorBoundary error={error} />}
    />
  );
}

十、环境变量

对于一些不能在 .umirc.ts 或 src/app.ts 中显式编写的配置信息,可以存储在环境变量文件中。

父应用

INITIAL_QIANKUN_MASTER_OPTIONS="{"apps":[{"name":"app1","entry":"//localhost:7001"},{"name":"app2","entry":"//localhost:7002"}]}"

子应用

INITIAL_QIANKUN_SLAVE_OPTIONS="{"enable":false}"

十一、API 参考

MasterOptions

属性必填说明类型默认值
enable启用 Qiankun 微应用插件,设为 false 时不启用booleanundefined
apps微应用配置App[]undefined
routes微应用运行时的路由Route[]undefined
defaultErrorBoundary子应用默认的错误捕获组件,值为文件路径string-
defaultLoader子应用默认的加载动画,值为文件路径string-
sandbox是否开启沙箱模式boolean{ strictStyleIsolation: boolean, experimentalStyleIsolation: boolean }true
prefetch是否启用微应用预加载boolean'all'string[](( apps: RegistrableApp[] ) => { criticalAppNames: string[]; minorAppsName: string[] })true

SlaveOptions

属性必填说明类型默认值
enable启用 Qiankun 微应用插件,设为 false 时不启用booleanundefined

App

属性必填说明类型默认值
name微应用的名称string
entry