一、什么是 Single-SPA
Single-SPA 是微前端领域的"鼻祖"框架,它是一个用于前端微服务化的 JavaScript 框架,允许你在同一个页面中使用多个框架(React、Vue、Angular 等),而不需要刷新页面。
核心定位
- JS Entry 方案:通过加载子应用的 JS 入口文件来集成
- 路由驱动:基于 URL 变化自动激活/卸载子应用
- 框架无关:支持任意前端框架,只要导出生命周期函数
二、核心概念
1. 三种模块类型
| 类型 | 说明 | 生命周期 | 使用场景 |
|---|---|---|---|
| Application | 有 UI 的微前端应用 | bootstrap → mount → unmount | 独立页面/功能模块 |
| Parcel | 可复用的 UI 组件 | 手动挂载/卸载 | 跨应用共享组件 |
| Utility | 无 UI 的共享模块 | 无 | 工具函数、API 封装、状态管理 |
2. 生命周期函数
每个 Application 必须导出三个生命周期函数:
javascript
// 应用首次加载时调用(只执行一次)
export async function bootstrap(props) {
console.log("应用初始化");
}
// 路由匹配时调用(每次激活都执行)
export async function mount(props) {
// 渲染 DOM
ReactDOM.createRoot(props.container).render(<App />);
}
// 路由离开时调用
export async function unmount(props) {
// 清理 DOM 和副作用
root.unmount();
}
3. 生命周期流程
makefile
首次加载: load → bootstrap → mount
路由切换: unmount → mount (另一个应用)
完全卸载: unmount → unload (可选)
三、核心原理
1. 路由劫持
Single-SPA 通过劫持浏览器的路由事件来实现应用切换:
javascript
// 劫持 history API
const originalPushState = window.history.pushState;
window.history.pushState = function (...args) {
originalPushState.apply(this, args);
// 触发应用切换逻辑
reroute();
};
// 监听 popstate 事件
window.addEventListener("popstate", reroute);
2. 应用状态机
每个应用都有状态流转:
markdown
NOT_LOADED → LOADING_SOURCE_CODE → NOT_BOOTSTRAPPED
→ BOOTSTRAPPING → NOT_MOUNTED → MOUNTING → MOUNTED
→ UNMOUNTING → NOT_MOUNTED
3. 应用加载与执行
javascript
// 注册应用
registerApplication({
name: "app1",
app: () => import("./app1/main.js"), // 动态导入
activeWhen: "/app1", // 激活条件
customProps: { authToken: "xxx" }, // 传递给子应用的 props
});
// 启动
start();
4. Import Map 模块解析
Single-SPA 推荐使用 Import Map 来管理模块 URL:
html
<script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js",
"@myorg/app1": "https://mycdn.com/app1/main.js"
}
}
</script>
浏览器会根据 Import Map 解析 import '@myorg/app1' 到实际 URL。
四、Single-SPA 生态
1. 核心包
| 包名 | 作用 |
|---|---|
single-spa |
核心库,提供注册、启动、生命周期管理 |
single-spa-layout |
声明式布局引擎,用 HTML 定义路由和布局 |
2. 框架适配器
| 适配器 | 框架 |
|---|---|
single-spa-react |
React |
single-spa-vue |
Vue 2/3 |
single-spa-angular |
Angular |
single-spa-svelte |
Svelte |
3. 开发工具
| 工具 | 作用 |
|---|---|
import-map-overrides |
运行时覆盖 Import Map,方便本地调试 |
import-map-injector |
支持从远程 URL 加载 Import Map |
create-single-spa |
官方脚手架 CLI |
五、项目结构
推荐的目录结构
bash
micro-frontends/
├── root-config/ # 基座应用
│ ├── src/
│ │ ├── index.ejs # HTML 模板 + 布局配置
│ │ └── root-config.js # 应用注册和启动
│ └── webpack.config.js
│
├── navbar/ # 导航栏应用(始终显示)
├── app1/ # 业务应用 1
├── app2/ # 业务应用 2
│
├── shared/ # 共享模块
│ ├── api/ # API 封装
│ └── styleguide/ # 公共样式/组件
│
└── shared-dependencies/ # Import Map 配置
└── importmap.json
六、创建 Single-SPA 项目
方式一:使用官方脚手架 create-single-spa(推荐)
bash
# 全局安装
npm install -g create-single-spa
# 或使用 npx
npx create-single-spa
脚手架会引导你选择:
-
项目类型:
single-spa root config- 基座应用single-spa application / parcel- 子应用in-browser utility module- 工具模块
-
框架:React / Vue / Angular / Svelte / None
-
包管理器:npm / yarn / pnpm
创建基座应用
bash
npx create-single-spa --moduleType root-config
# 选择组织名称,如 @myorg
创建 React 子应用
bash
npx create-single-spa --moduleType app-parcel --framework react
创建 Vue 子应用
bash
npx create-single-spa --moduleType app-parcel --framework vue
方式二:手动配置
如果需要更多控制,可以手动配置:
1. 基座应用 (root-config)
bash
mkdir root-config && cd root-config
npm init -y
npm install single-spa single-spa-layout
npm install -D webpack webpack-cli webpack-dev-server html-webpack-plugin
javascript
// src/root-config.js
import { registerApplication, start } from "single-spa";
import {
constructApplications,
constructRoutes,
constructLayoutEngine,
} from "single-spa-layout";
const routes = constructRoutes(document.querySelector("#single-spa-layout"));
const applications = constructApplications({
routes,
loadApp: ({ name }) => import(/* webpackIgnore: true */ name),
});
applications.forEach(registerApplication);
constructLayoutEngine({ routes, applications }).activate();
start();
2. React 子应用
bash
npm install single-spa-react react react-dom
javascript
// src/main.js
import React from "react";
import ReactDOMClient from "react-dom/client";
import singleSpaReact from "single-spa-react";
import App from "./App";
const lifecycles = singleSpaReact({
React,
ReactDOMClient,
rootComponent: App,
errorBoundary(err, info, props) {
return <div>Error</div>;
},
});
export const { bootstrap, mount, unmount } = lifecycles;
3. Vue 子应用
bash
npm install single-spa-vue vue
javascript
// src/main.js
import { createApp, h } from "vue";
import singleSpaVue from "single-spa-vue";
import App from "./App.vue";
const vueLifecycles = singleSpaVue({
createApp,
appOptions: {
render() {
return h(App);
},
},
});
export const { bootstrap, mount, unmount } = vueLifecycles;
方式三:参考官方示例仓库
| 示例 | 地址 | 说明 |
|---|---|---|
| React 示例 | github.com/react-micro... | 完整的 React 微前端示例 |
| Vue 示例 | github.com/vue-microfr... | 完整的 Vue 微前端示例 |
| 混合框架示例 | github.com/polyglot-mi... | React + Vue + Angular 混合 |
在线演示:
- React: react.microfrontends.app
- Vue: vue.microfrontends.app
七、开发调试技巧
1. 使用 import-map-overrides
javascript
// 在浏览器控制台启用开发工具
localStorage.setItem("devtools", true);
// 刷新页面后,右下角会出现开发工具面板
// 可以将任意模块指向本地开发服务器
2. 本地开发流程
bash
# 1. 启动本地子应用
cd my-app && npm start --port 9001
# 2. 访问线上/本地基座
# 3. 使用 import-map-overrides 将 @myorg/my-app 指向 localhost:9001
3. 独立运行子应用
子应用应该能够独立运行,方便开发调试:
javascript
// 判断是否在 single-spa 环境中
if (!window.singleSpaNavigate) {
// 独立运行
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
}
八、Single-SPA vs 其他方案
| 特性 | single-spa | qiankun | micro-app |
|---|---|---|---|
| Entry 类型 | JS Entry | HTML Entry | HTML Entry |
| 沙箱隔离 | ❌ 无内置 | ✅ Proxy 沙箱 | ✅ 沙箱隔离 |
| 样式隔离 | ❌ 无内置 | ✅ Shadow DOM / Scoped | ✅ 样式隔离 |
| 老项目改造 | 较高 | 极低 | 极低 |
| 灵活性 | ★★★★★ | ★★★★ | ★★★ |
| 学习曲线 | 较陡 | 中等 | 较平缓 |
选择建议
- 选 single-spa:需要最大灵活性、深入理解微前端原理、技术能力强的团队
- 选 qiankun:企业级项目、多历史系统接入、需要开箱即用的沙箱和隔离
- 选 micro-app:快速上手、组件化思维、Vue 技术栈为主
九、最佳实践
1. 共享依赖
通过 Import Map 共享公共依赖,避免重复加载:
json
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react@18/...",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@18/..."
}
}
2. 样式隔离
Single-SPA 不提供内置样式隔离,需要自行处理:
- CSS Modules
- CSS-in-JS (styled-components, emotion)
- BEM 命名规范
- 添加应用前缀
3. 全局状态管理
- 使用 Utility 模块共享状态
- 发布订阅模式 (EventBus)
- 通过 customProps 传递
4. 错误边界
javascript
singleSpaReact({
// ...
errorBoundary(err, info, props) {
return <ErrorPage error={err} />;
},
});
十、学习资源
官方资源
- 官方文档: single-spa.js.org/docs/gettin...
- 推荐设置: single-spa.js.org/docs/recomm...
- GitHub: github.com/single-spa/...
视频教程
- YouTube 官方教程: www.youtube.com/playlist?li...
- B 站教程: www.bilibili.com/video/av834...
相关规范
- Import Map 规范: github.com/WICG/import...
- ES Modules: developer.mozilla.org/en-US/docs/...