Skip to main content

微前端生命周期解析

微前端架构通过解耦单体应用为多个独立子应用,实现了技术栈无关性、独立部署与团队自治等核心价值。然而,其核心挑战在于如何协调主应用与子应用的生命周期,确保资源加载、环境隔离与状态管理的无缝衔接。

一、主应用生命周期

主应用作为微前端架构的核心调度者,负责全局配置、子应用注册与资源调度,其生命周期包含以下关键阶段:

1. 全局配置初始化(start)

qiankun 通过 start(opts) 方法初始化全局参数,包括:

  • 预加载策略(prefetch):根据 prefetch 参数决定是否在首个子应用挂载后预加载其他子应用资源,通过监听 single-spa:first-mount 事件触发。
  • 沙箱模式(sandbox):通过 sandbox 参数启用 JS 沙箱环境,支持 LegacySandbox(单实例)和 ProxySandbox(多实例),并根据浏览器兼容性自动降级为 SnapshotSandbox
  • 单实例模式(singular)singular 参数控制是否允许同时运行多个子应用,默认单实例模式下,新子应用挂载前会卸载旧应用。
start({
// 预加载策略
prefetch: 'all',
sandbox: {
// CSS严格隔离(Shadow DOM)
strictStyleIsolation: true,
// 实验性样式隔离(CSS前缀注入)
experimentalStyleIsolation: true
},
// 单实例模式
singular: true
})

2. 子应用注册(registerMicroApps)

主应用通过 registerMicroApps 注册子应用,核心配置包括:

  • 路由匹配规则(activeRule):基于 activeRule 定义子应用激活的 URL 路径,如 genActiveRule('/app1') 匹配 /app1/**
  • 生命周期钩子注入:定义全局钩子(beforeLoadbeforeMountafterUnmount),用于统一处理权限校验、埋点上报等横切关注点。
  • 资源加载入口:通过 entry 指定子应用的 HTML 入口地址,qiankun 使用 import-html-entry 库解析 HTML,提取 JS/CSS 资源并执行脚本。

3. 完整示例

下面以 React 主应用为例,改造入口文件:

main-app/src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { registerMicroApps, start } from "qiankun";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
// 主应用渲染函数
function renderMainApp() {
root.render(
<React.StrictMode>
<BrowserRouter>
{/* 子应用容器需确保存在 App 中 */}
<App />
</BrowserRouter>
</React.StrictMode>
);
}

// 注册微应用配置
const microApps = [
{
name: "app1-react", // 子应用名称(需与子应用 package.json 中的 name 一致)
entry: "//localhost:7101", // 子应用入口(开发环境地址需与子应用服务端口一致)
container: "#subapp-container", // 子应用挂载容器(需要存在于主应用 DOM 中)
activeRule: "/app1-react", // 子应用路由激活规则
},
{
name: "app2-vue",
entry: "//localhost:7102",
container: "#subapp-container",
activeRule: "/app2-vue",
},
];

// 启动配置
function initQiankun() {
// 注册微应用
registerMicroApps(microApps, {
beforeLoad: [(app) => console.log("checkAuth:", app.name)],
beforeMount: [(app) => console.log("initAnalytics:", app.name)],
afterMount: [(app) => console.log("After mount:", app.name)],
beforeUnmount: [(app) => console.log("Before unmount:", app.name)],
afterUnmount: [(app) => console.log("clearCache:", app.name)],
});

// 启动 qiankun
start({
// 预加载策略
prefetch: process.env.NODE_ENV === 'production' ? 'all' : false,
sandbox: {
// CSS严格隔离(Shadow DOM)
strictStyleIsolation: true,
// 实验性样式隔离(CSS前缀注入)
experimentalStyleIsolation: true
},
// 单实例模式
singular: true
});
}

// 初始化流程
function init() {
renderMainApp(); // 先渲染主应用
initQiankun(); // 再初始化 Qiankun
}

// 启动应用
init();

二、子应用生命周期

子应用需暴露标准生命周期钩子函数,供主应用调度。qiankun 基于 single-spa 规范扩展了以下阶段:

1. Bootstrap(初始化)

子应用首次加载时执行一次,用于初始化全局状态或执行预加载逻辑。例如:

export async function bootstrap(props) {
console.log('子应用初始化', props);
}

2. Mount(挂载)

子应用激活时触发,负责渲染 DOM 并绑定事件。qiankun 在此阶段完成:

  • HTML 模板挂载:将解析后的子应用 HTML 插入主应用容器(如 container.querySelector('#subapp'))。
  • 沙箱环境激活:创建或恢复 JS 沙箱(如 LegacySandbox 通过 Proxy 代理 window 对象),确保全局变量隔离。
  • 框架渲染触发:执行子应用的框架挂载方法(如 Vue 的 $mount() 或 React 的 ReactDOM.render())。

3. Unmount(卸载)

子应用切换时触发,用于销毁实例、清理事件监听及还原全局状态。qiankun 在此阶段:

  • 框架实例销毁:调用如 Vue.$destroy()ReactDOM.unmountComponentAtNode()
  • 沙箱环境重置:清除子应用对 window 的修改,恢复主应用全局状态。

4. Update(更新,可选)

子应用状态变化时触发,用于响应主应用传递的新属性(如用户权限变更)。

5. 示例

app1-react/src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";

let root = null;

// 独立运行逻辑(开发环境直接渲染)
// 通过 window.__POWERED_BY_QIANKUN__ 识别是否被 Qiankun 加载
if (process.env.NODE_ENV === "development" && !window.__POWERED_BY_QIANKUN__) {
const container = document.getElementById("root");
root = ReactDOM.createRoot(container);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
}

// 导出 Qiankun 生命周期
export async function bootstrap() {
console.log("[React] app1 bootstraped");
}

// mount() 动态挂载
// 使用主应用传入的 container 挂载子应用,避免 DOM 冲突
export async function mount(props) {
console.log("[React] mount app1", props);
const { container } = props;

// 直接使用主应用传递的 container
const dom = container ? container : document.getElementById("root");

root = ReactDOM.createRoot(dom);
root.render(
<BrowserRouter
basename={window.__POWERED_BY_QIANKUN__ ? "/app1-react" : "/"}
>
<App />
</BrowserRouter>
);
}

// unmount() 清理资源
// 确保子应用卸载时释放内存,防止内存泄漏
export async function unmount() {
console.log("[React] Unmount");
root?.unmount();
root = null;
}

// 性能监控(可选)
reportWebVitals();