从零理解微前端:基于 React + Vite + qiankun 的子应用切换 Demo

本文基于当前 micro-demo 项目,介绍什么是微前端、为什么要使用微前端,以及这个 Demo 如何通过 qiankun 实现一个 React 基座应用加载 React 子应用。

项目结构如下:

text 复制代码
micro-demo
├── main-react   # 微前端基座应用
└── sub-react    # React 子应用

1. 什么是微前端

微前端可以理解为前端领域里的"微服务"思想:把一个大型前端应用拆成多个可以独立开发、独立运行、独立构建、独立部署的小应用,再由一个主应用把它们组合起来。

在传统单体前端中,所有页面、路由、状态、依赖和构建流程通常都放在同一个项目里。随着业务增长,这种模式会遇到一些问题:

  • 项目越来越大,启动和构建变慢。
  • 多个团队在同一个代码仓库里协作,容易互相影响。
  • 技术栈升级困难,例如想把部分旧页面从 Vue 迁移到 React。
  • 某个业务模块上线,需要跟整个前端项目一起发版。

微前端的核心目标就是降低这些耦合。它允许不同业务模块以子应用的形式存在,每个子应用可以有自己的技术栈、依赖、路由和发布节奏。

2. 微前端解决什么问题

微前端并不是为了让项目变复杂,而是为了解决中大型前端项目中的组织和演进问题。

常见使用场景包括:

  • 多团队协作:不同团队负责不同子应用,减少代码冲突。
  • 老项目渐进式改造:保留原有系统,同时逐步迁移部分模块。
  • 多技术栈共存:主应用可以加载 React、Vue、Angular 等不同技术栈的子应用。
  • 独立部署:一个子应用更新,不必重新发布所有前端代码。
  • 业务边界清晰:按业务域拆分应用,例如订单、用户、报表、权限等模块。

当然,微前端也有成本,例如运行时集成、样式隔离、依赖重复、通信约定、部署配置等。因此,小型项目通常不需要一开始就引入微前端。

3. qiankun 的基本思路

当前项目使用的是 qiankun。qiankun 是一个基于 single-spa 的微前端框架,它主要负责几件事:

  • 注册子应用。
  • 根据当前 URL 判断应该激活哪个子应用。
  • 加载子应用入口资源。
  • 把子应用挂载到主应用指定的 DOM 容器中。
  • 在切换路由时卸载旧子应用。
  • 提供生命周期机制,例如 bootstrapmountunmount

在 qiankun 模式下,通常会有两类应用:

  • 基座应用:负责整体布局、导航、注册子应用和提供挂载容器。
  • 子应用:负责自己的页面内容,并暴露符合 qiankun 约定的生命周期。

当前 Demo 中:

  • main-react 是基座应用。
  • sub-react 是子应用。
  • 访问基座的 /sub-react 路径时,qiankun 会加载 sub-react

4. 当前项目的目录说明

4.1 基座应用 main-react

main-react 的关键文件:

text 复制代码
main-react
├── index.html
├── package.json
├── vite.config.js
└── src
    ├── main.jsx
    └── style.css

它依赖:

json 复制代码
{
  "qiankun": "^2.10.16",
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}

基座应用的职责是:

  • 渲染页面标题和导航。
  • 提供 #subapp-container 作为子应用挂载容器。
  • 使用 registerMicroApps 注册子应用。
  • 调用 start() 启动 qiankun。

4.2 子应用 sub-react

sub-react 的关键文件:

text 复制代码
sub-react
├── index.html
├── package.json
├── vite.config.js
└── src
    ├── main.jsx
    └── App.jsx

它依赖:

json 复制代码
{
  "vite-plugin-qiankun": "^1.0.15",
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}

子应用的职责是:

  • 在独立运行时,可以自己渲染到自己的 #root
  • 被 qiankun 加载时,按照 qiankun 生命周期挂载和卸载。
  • 通过 vite-plugin-qiankun 适配 Vite 构建和 qiankun 运行时。

5. 如何运行这个 Demo

这个 Demo 需要同时启动基座应用和子应用。

先启动子应用:

bash 复制代码
cd sub-react
npm run dev

子应用端口在 sub-react/vite.config.js 中被固定为 8001

js 复制代码
server: {
  port: 8001,
  cors: true,
}

然后启动基座应用:

bash 复制代码
cd ../main-react
npm run dev

基座应用没有固定端口,Vite 默认通常会启动在:

text 复制代码
http://localhost:5173

打开基座地址后:

  • 访问 / 时显示基座首页。
  • 点击"React 子应用"后,URL 变成 /sub-react
  • qiankun 命中子应用的 activeRule,加载 http://localhost:8001
  • 子应用内容被渲染到基座的 #subapp-container 中。

6. 基座应用如何实现微前端加载

基座核心代码在 main-react/src/main.jsx

首先,基座正常渲染一个 React 应用:

jsx 复制代码
function App() {
  return (
    <div>
      <h1>React18 微前端基座</h1>

      <nav>
        <a href="/">首页</a>
        <a href="/sub-react">React 子应用</a>
      </nav>

      <div id="subapp-container">
        <h2>基座首页</h2>
        <p>点击上方菜单加载 React 子应用。</p>
      </div>
    </div>
  );
}

这里最重要的是这个容器:

html 复制代码
<div id="subapp-container"></div>

它是子应用最终要挂载的位置。

然后基座注册子应用:

jsx 复制代码
registerMicroApps([
  {
    name: "sub-react",
    entry: "//localhost:8001",
    container: "#subapp-container",
    activeRule: "/sub-react",
  },
]);

这段配置的含义是:

字段 含义
name 子应用名称,需要保持唯一
entry 子应用入口地址,这里是 //localhost:8001
container 子应用要挂载到基座的哪个 DOM 容器
activeRule 当 URL 匹配 /sub-react 时激活这个子应用

最后启动 qiankun:

jsx 复制代码
start();

调用 start() 后,qiankun 会开始监听路由变化。当浏览器地址匹配 /sub-react 时,就会请求并加载子应用资源。

7. 子应用如何接入 qiankun

子应用核心代码在 sub-react/src/main.jsx

子应用首先定义自己的渲染函数:

jsx 复制代码
function render(container) {
  const rootElement = container
    ? container.querySelector("#root")
    : document.querySelector("#root");

  root = ReactDOM.createRoot(rootElement);
  root.render(<App />);
}

这里有一个关键判断:

  • 如果有 container,说明当前是被 qiankun 加载,应该在 qiankun 提供的容器里找 #root
  • 如果没有 container,说明当前是子应用独立运行,直接从 document 上找 #root

然后子应用通过 renderWithQiankun 暴露生命周期:

jsx 复制代码
renderWithQiankun({
  bootstrap() {
    console.log("sub-react bootstrap");
  },
  mount(props) {
    console.log("sub-react mount", props);
    render(props.container);
  },
  unmount() {
    console.log("sub-react unmount");
    root.unmount();
    root = null;
  },
});

这三个生命周期分别表示:

生命周期 触发时机 当前项目中的作用
bootstrap 子应用第一次初始化时 打印初始化日志
mount 子应用被激活时 调用 render 渲染 React 应用
unmount 子应用被切走时 调用 root.unmount() 卸载 React 应用

最后这段代码保证子应用可以独立运行:

jsx 复制代码
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render();
}

如果不是在 qiankun 环境下运行,就直接执行 render()。因此 sub-react 既可以被基座加载,也可以直接访问 http://localhost:8001 独立运行。

8. 子应用的 Vite 配置

子应用的配置在 sub-react/vite.config.js

js 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import qiankun from "vite-plugin-qiankun";

export default defineConfig({
  plugins: [
    react(),
    qiankun("sub-react", {
      useDevMode: true,
    }),
  ],
  server: {
    port: 8001,
    cors: true,
  },
  base: "http://localhost:8001/",
});

这里有三个重点。

第一,使用 vite-plugin-qiankun

js 复制代码
qiankun("sub-react", {
  useDevMode: true,
})

它帮助 Vite 子应用适配 qiankun 的加载方式。

第二,固定开发端口:

js 复制代码
server: {
  port: 8001,
  cors: true,
}

基座的 entry 写的是 //localhost:8001,所以子应用必须运行在 8001 端口。cors: true 是为了允许基座跨端口加载子应用资源。

第三,配置资源基础路径:

js 复制代码
base: "http://localhost:8001/",

这样子应用资源在被基座加载时,可以从正确的地址请求。

9. 一次完整的切换流程

以用户点击"React 子应用"为例,完整流程如下:

  1. 用户打开基座应用,例如 http://localhost:5173
  2. 基座渲染标题、导航和 #subapp-container 容器。
  3. 基座调用 registerMicroApps 注册 sub-react
  4. 基座调用 start() 启动 qiankun。
  5. 用户点击导航链接 /sub-react
  6. 浏览器 URL 变为 http://localhost:5173/sub-react
  7. qiankun 发现当前路径匹配 activeRule: "/sub-react"
  8. qiankun 请求子应用入口 //localhost:8001
  9. 子应用资源加载完成后,执行 bootstrap
  10. qiankun 调用子应用的 mount(props)
  11. 子应用从 props.container 中找到自己的 #root
  12. 子应用通过 ReactDOM.createRoot 渲染 App
  13. 页面中 #subapp-container 的内容被替换为子应用内容。
  14. 用户回到 / 或切换到其他子应用时,qiankun 调用 unmount 卸载当前子应用。

这就是当前 Demo 中"点击菜单切换到子应用"的完整链路。

10. 当前 Demo 的特点和边界

这个 Demo 已经覆盖了微前端最核心的流程:

  • 基座应用注册子应用。
  • 子应用支持 qiankun 生命周期。
  • 子应用可以被基座加载。
  • 子应用也可以独立运行。
  • 通过 URL 路由完成应用切换。

不过它还是一个最小示例,暂时没有处理一些生产环境中常见的问题:

  • 多个子应用的注册和切换。
  • 基座和子应用之间的数据通信。
  • 样式隔离和全局样式污染。
  • 公共依赖复用。
  • 权限、菜单、面包屑等基座能力下发。
  • 生产环境部署路径和 Nginx 配置。
  • 子应用加载失败、loading、错误兜底。

这些内容通常会在真实业务中逐步补齐。

11. 如果继续扩展这个项目

可以按下面几个方向继续完善:

  1. 增加第二个子应用,例如 sub-vuesub-react-admin
  2. 给基座增加统一布局,例如侧边栏、顶部栏和内容区。
  3. 使用 qiankun 的 props 给子应用传递用户信息、主题配置或公共方法。
  4. 使用 initGlobalState 实现基座和子应用之间的状态通信。
  5. #subapp-container 增加 loading 和错误兜底。
  6. 为子应用增加独立路由,演示 /sub-react/list/sub-react/detail 等页面。
  7. 编写生产部署说明,让基座和子应用可以分别构建、分别部署。

12. 总结

微前端的本质是把一个大型前端系统拆成多个可以独立交付的小应用,再通过基座应用在运行时把它们组合起来。

在当前项目中:

  • main-react 扮演基座角色。
  • sub-react 扮演子应用角色。
  • 基座通过 registerMicroApps 注册子应用。
  • activeRule: "/sub-react" 决定什么时候加载子应用。
  • container: "#subapp-container" 决定子应用挂载到哪里。
  • 子应用通过 renderWithQiankun 暴露 bootstrapmountunmount 生命周期。
  • qiankunWindow.__POWERED_BY_QIANKUN__ 用来判断当前是独立运行还是被基座加载。

这个 Demo 虽然简单,但已经体现了微前端的核心闭环:基座注册、路由匹配、资源加载、生命周期挂载、切换卸载。

相关推荐
2601_957780841 小时前
AI智能体时代:为什么HTML正在取代Markdown成为新一代输出标准
大数据·前端·人工智能·gpt·html·claude
2301_815279522 小时前
如何实现C++ Web 自动化测试实战:常用函数全解析与场景化应用指南
开发语言·前端·c++
代码不停2 小时前
Spring Web MVC
前端·spring·mvc
倾颜8 小时前
从 textarea 到 AI 输入框:用 Tiptap 实现 / 命令、@ 引用和结构化请求
前端·langchain·next.js
kyriewen9 小时前
程序员连夜带团队跑路,省了23万:这AI太贵,真的用不起了
前端·javascript·openai
kyriewen10 小时前
你写的代码没有测试,就像出门不锁门——Jest + Testing Library 从入门到不慌
前端·单元测试·jest
yuzhiboyouye10 小时前
web前端英语面试
前端·面试·状态模式
canonical_entropy12 小时前
下一代低代码渲染框架 nop-chaos-flux 的设计原则
前端·低代码·前端框架
东方小月12 小时前
5分钟搞懂Harness Engineering(驾驭工程):从提示词到AI Agent的进化之路
前端·后端·架构