业务系统越做越大,是很多团队迟早要面对的现实。一个React单页面应用从最初的十几个页面膨胀到上百个页面之后,构建时间从一分钟涨到十几分钟,任何一个小组的功能改动都可能引发全量回归测试,发布窗口被挤得死死的。微前端的核心思路是把一个大应用拆成若干个可以独立开发、独立构建、独立部署的小应用,运行时再组合到一起。目前在React生态里,single-spa和qiankun是最主流的两个落地框架,本文将结合实际代码,讲清楚二者的关系以及完整的集成过程。

一、先理清single-spa和qiankun的关系
不少初学者会把这两个框架当成竞争关系,其实更准确的说法是qiankun是single-spa的二次封装。single-spa提供的是一套微前端的运行时协议:它定义了子应用必须暴露的bootstrap、mount、unmount三个生命周期函数,以及路由劫持、应用激活规则等基础能力。但它对子应用的打包产物格式有严格要求,你需要自己处理JS和CSS的加载、全局变量污染、样式冲突等琐碎问题。
qiankun在single-spa之上补齐了这些工程化短板。它通过import-html-entry拉取子应用的HTML入口,自动解析出其中的JS和CSS资源并执行,同时提供HTML StyleSandbox(样式沙箱)和JS Sandbox(三种模式的JS沙箱)来隔离父子应用的运行环境。简单说,如果你只想快速跑通微前端,直接用qiankun;如果你想精细控制加载过程,或者子应用形态比较特殊,可以基于single-spa自己组装。
二、基于single-spa搭建React微前端基座
1. 主应用(基座)的搭建
主应用负责注册所有子应用并根据URL决定激活哪一个。先安装依赖:
npm install single-spa single-spa-react
注册子应用的核心代码如下:
import { registerApplication, start } from 'single-spa';
registerApplication({
name: 'app-react',
app: () => import('./src/appReact.main.js'),
activeWhen: location => location.pathname.startsWith('/react-app'),
});
registerApplication({
name: 'app-vue',
app: () => System.import('http://localhost:8081/main.js'),
activeWhen: ['/vue-app'],
});
start();activeWhen支持函数、字符串前缀和数组三种形式,当URL匹配时single-spa会依次调用子应用的bootstrap和mount,切换离开时调用unmount。这里需要注意,主应用的路由必须和子应用的激活规则协调好,否则会出现主子应用同时响应同一路由的情况。
2. 子应用的改造
子应用使用single-spa-react生成生命周期钩子,并独立启动React:
import React from 'react';
import ReactDOM from 'react-dom/client';
import singleSpaReact from 'single-spa-react';
import App from './App';
const lifecycles = singleSpaReact({
React,
ReactDOMClient: ReactDOM,
rootComponent: App,
errorBoundary() {
return <div>子应用渲染出错了</div>;
},
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;webpack配置需要调整输出格式为library,并把single-spa相关的依赖声明为外部依赖,避免被重复打包:
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: 'module',
publicPath: '//localhost:8082/',
},
externals: ['single-spa-react']三、用qiankun快速集成React子应用
相比single-spa,qiankun的接入成本明显更低。主应用只需要安装qiankun,然后用registerMicroApps注册子应用清单:
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:8082',
container: '#subapp-container',
activeRule: '/react-app',
props: { token: 'xxx', getUserInfo: () => ({ name: '张三' }) },
},
]);
start({ prefetch: true });qiankun会自动拉取entry指向的HTML,解析出资源清单并执行。子应用需要在入口文件导出async的生命周期,并在webpack中设置关键的跨域响应头处理:
export async function bootstrap() {}
export async function mount(props) {
ReactDOM.createRoot(props.container
? props.container.querySelector('#root')
: document.getElementById('root')).render(<App />);
}
export async function unmount() {}开发环境还需要在devServer中配置headers,允许主应用跨域加载资源:
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
}四、生产环境必须面对的三个问题
1. 样式与JS隔离
qiankun默认开启样式沙箱,子应用挂载时会给样式规则添加特殊前缀,卸载时移除。但主应用自身的样式仍可能影响子应用,实践中建议约定一套CSS命名规范,或者直接使用CSS Modules、CSS-in-JS从源头避免冲突。JS沙箱有三种模式:legacySandbox适合单实例场景,proxySandbox适合多实例并存,snapshotSandbox兼容不支持Proxy的低版本浏览器,按需选择即可。
2. 公共依赖抽取
React、react-dom这类被所有子应用共享的库,如果每个子应用都打一份,既浪费体积又可能导致多实例冲突。qiankun提供了excludeAssetFilter过滤配置,配合externals和CDN加载,可以让子应用复用主应用已经加载的React。另外一个思路是使用webpack的模块联邦(Module Federation),它和微前端并不冲突,可以组合使用。
3. 应用间通信
qiankun的initGlobalState提供了简单的全局状态共享,本质上是一个发布订阅模型:
// 主应用
import { initGlobalState } from 'qiankun';
const actions = initGlobalState({ user: null });
actions.onGlobalStateChange((state, prev) => {
console.log('主应用监听到变化', prev, '=>', state);
});
// 子应用 mount 中
export function mount(props) {
props.onGlobalStateChange((state) => {
console.log('子应用收到', state);
});
props.setGlobalState({ user: { name: '张三' } });
}如果通信场景复杂,也可以直接引入Redux、Zustand等状态库放在主应用,通过props把store实例传给各子应用,管理起来更直观。
五、总结
single-spa给出了微前端的底层协议,qiankun降低了工程落地门槛,两者结合React使用时,核心工作集中在三处:生命周期函数的正确导出、webpack输出格式与跨域配置、以及沙箱和通信方案的选择。拆分粒度上建议按业务域而非页面拆分,一个子应用保持一个独立可交付的业务单元,这样才能真正享受到独立部署带来的收益。同时要警惕过度拆分,子应用数量过多会让联调和排查问题的成本快速上升,通常两到五个子应用是比较健康的规模。
微前端single-spaqiankun修改时间:2026-09-05 23:37:04