微前端架构将大型前端应用拆分为多个独立开发、独立部署的子应用,解决巨石应用的构建慢、部署耦合和团队协作问题。qiankun和Module Federation是两种主流的微前端方案,前者基于HTML入口和JS沙箱实现运行时集成,后者基于Webpack 5联邦模块实现构建时依赖共享。两种方案在应用隔离、通信机制和部署模式上差异显著,选型需结合团队技术栈和工程化成熟度综合考量。
微前端架构模式与核心设计目标
微前端架构的核心设计目标包括:技术栈无关(子应用可使用不同框架)、独立开发部署(子应用独立仓库独立CI/CD)、运行时集成(用户无感知的子应用切换)、样式隔离(子应用CSS互不影响)、JS隔离(子应用全局变量互不污染)。
微前端的三种集成模式:基座模式(一个主应用作为容器动态加载子应用)、自组织模式(无主应用,子应用之间直接路由跳转)、NPM包模式(子应用编译为NPM包由主应用安装)。qiankun采用基座模式,Module Federation支持基座和NPM两种模式。
qiankun框架实现:JS沙箱与CSS隔离方案
qiankun基于single-spa,通过HTML entry方式加载子应用,在运行时动态创建JS沙箱隔离子应用全局环境。主应用注册子应用配置:
// 主应用 main.js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:7100',
container: '#sub-app-container',
activeRule: '/react',
props: {
token: localStorage.getItem('token'),
onRouteChange: (path) => console.log('子应用路由:', path)
}
},
{
name: 'vue-app',
entry: '//localhost:7101',
container: '#sub-app-container',
activeRule: '/vue',
}
]);
start({
prefetch: true,
sandbox: {
strictStyleIsolation: true,
experimentalStyleIsolation: true
}
});
// 子应用 React - 入口配置
export async function mount(props) {
const container = props.container;
ReactDOM.render(
<App />,
container.querySelector('#root')
);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(
container.querySelector('#root')
);
}
export async function bootstrap() {
console.log('react-app bootstrap');
}
// 子应用需修改webpack配置支持跨域和UMD输出
module.exports = {
output: {
library: 'react-app',
libraryTarget: 'umd',
jsonpFunction: 'webpackJsonp_react_app'
},
devServer: {
headers: { 'Access-Control-Allow-Origin': '*' }
}
}
qiankun的JS沙箱通过Proxy代理window对象实现:子应用对window的读写被拦截并存储在独立的沙箱上下文中,卸载时自动清除,避免全局变量污染。qiankun支持两种CSS隔离策略:Shadow DOM隔离(将子应用挂载到Shadow Root内,完全隔离样式但第三方组件库可能失效)和Scoped CSS隔离(为子应用CSS选择器添加属性前缀,兼容性更好但无法处理动态样式)。
Module Federation方案:Webpack 5联邦模块运行时加载
Module Federation是Webpack 5内置功能,允许不同Webpack构建之间在运行时共享模块。一个应用可以暴露模块供其他应用消费,也可以消费其他应用暴露的模块。与qiankun不同,Module Federation在构建时声明依赖关系,运行时按需加载:
// 宿主应用 webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
reactApp: 'react_app@http://localhost:7100/remoteEntry.js',
vueApp: 'vue_app@http://localhost:7101/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true },
'react-router-dom': { singleton: true },
}
})
]
};
// 宿主应用动态加载子应用组件
import React, { Suspense } from 'react';
const ReactApp = React.lazy(() => import('reactApp/App'));
const VueApp = React.lazy(() => import('vueApp/App'));
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/react/*" element={
<Suspense fallback={<div>Loading...</div>}>
<ReactApp />
</Suspense>
} />
<Route path="/vue/*" element={
<Suspense fallback={<div>Loading...</div>}>
<VueApp />
</Suspense>
} />
</Routes>
</BrowserRouter>
);
}
// 子应用 webpack.config.js - 暴露组件
new ModuleFederationPlugin({
name: 'react_app',
filename: 'remoteEntry.js',
exposes: {
'./App': './src/App',
'./Button': './src/components/Button',
'./utils': './src/utils'
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true },
}
})
Module Federation的shared配置实现依赖共享:react、react-dom等公共库在宿主和子应用间共享同一实例,避免重复加载和React多实例报错问题。singleton: true确保全局只加载一个共享模块版本。版本不兼容时,Module Federation会回退到子应用自带的依赖副本。
子应用通信机制:qiankun globalState与MF共享模块对比
微前端架构中子应用通信是核心难点。qiankun通过initGlobalState提供全局状态通信API:
// 主应用初始化全局状态
import { initGlobalState } from 'qiankun';
const { onGlobalStateChange, setGlobalState } = initGlobalState({
user: { name: '', role: '' },
theme: 'light'
});
// 主应用监听子应用状态变更
onGlobalStateChange((state, prev) => {
console.log('全局状态变更:', state);
});
// 子应用通过props接收通信方法
export async function mount(props) {
// 监听主应用状态
props.onGlobalStateChange((state) => {
console.log('收到主应用状态:', state);
});
// 子应用向主应用发送状态
props.setGlobalState({
user: { name: '张三', role: 'admin' }
});
}
// Module Federation通信方案:共享EventBus
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
(this.events[event] = this.events[event] || []).push(callback);
}
emit(event, data) {
(this.events[event] || []).forEach(cb => cb(data));
}
off(event, callback) {
this.events[event] = (this.events[event] || [])
.filter(cb => cb !== callback);
}
}
export default new EventBus();
// 在webpack shared中共享EventBus
shared: {
'./eventBus': { singleton: true }
}
qiankun的通信方式适合简单状态同步场景,复杂状态管理需要配合Redux或Pinia等状态库。Module Federation通过共享模块实现通信更灵活,但需要开发者自行设计通信协议和状态管理方案。
微前端部署与路由分发配置
微前端部署的核心是路由分发:Nginx根据URL路径将请求代理到不同子应用服务器:
# Nginx配置 - 微前端路由分发
server {
listen 80;
server_name app.example.com;
# 主应用
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
}
# React子应用静态资源
location /react {
proxy_pass http://localhost:7100;
try_files $uri /index.html;
}
# Vue子应用静态资源
location /vue {
proxy_pass http://localhost:7101;
try_files $uri /index.html;
}
# 子应用静态资源CDN路径
location /static/react/ {
proxy_pass http://react-cdn.example.com/;
}
location /static/vue/ {
proxy_pass http://vue-cdn.example.com/;
}
}
qiankun和Module Federation的选型建议:技术栈统一且使用Webpack 5的项目优先选择Module Federation,依赖共享和构建时声明能减少运行时开销;技术栈异构(React+Vue+Angular混合)或需要更强隔离性的场景选择qiankun,JS沙箱和CSS隔离方案更成熟。两种方案都支持渐进式迁移:先接入一个子应用验证可行性,再逐步将巨石应用拆分为多个子应用。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/wei-qian-duan-jia-gou-shi-zhan-qiankun-yu-modulefederation/