屎山代码拆不动?微前端来救场:一个应用变“乐高城堡”
前言
想象你有一座巨大的乐高城堡,一开始几个人拼得很开心。后来城堡越拼越大,几百人同时在上面加砖,有人碰倒了塔楼,有人改错了城墙,整个城堡摇摇欲坠。你想拆成几个独立的小城堡,又怕它们之间连不起来。
这就是巨石前端的困境。微前端就是解决方案:把大应用拆成多个小应用(子应用),每个小应用独立开发、独立部署,最后在浏览器里组合成一个完整页面。就像乐高套装里的每个小模块,可以单独拼好,再插到一起。
一、什么时候需要微前端?
- 项目太大,编译部署一次要10分钟。
- 团队太多,几十人改同一个仓库,Git冲突到崩溃。
- 想渐进式升级技术栈(比如老项目用AngularJS,新模块用React)。
- 不同团队负责不同业务板块,希望独立发布互不干扰。
如果你的项目只有三五个人,别用微前端——杀鸡不用牛刀。
二、微前端三大核心问题
微前端要解决三个问题:
- 怎么加载子应用?(路由分发)
- 怎么隔离子应用?(JS沙箱、样式隔离)
- 怎么通信?(全局状态、事件总线)
三、常见实现方式
1. 路由分发式(Nginx反向代理)
不同路径对应不同子应用,比如/app1 → 应用1,/app2 → 应用2。父页面通过iframe或服务端路由组合。
- 简单,但切换应用会刷新页面。
- 不适合需要无缝组合的场景。
2. iframe:最土的“隔离神器”
iframe天然隔离JS和CSS,但缺点明显:通信麻烦、SEO差、弹窗无法覆盖、全局状态不共享。
3. single-spa:微前端的“老大哥”
一个框架,帮你管理子应用的加载、挂载、卸载。你需要自己写如何加载子应用(比如动态script加载),以及子应用暴露的生命周期(bootstrap、mount、unmount)。
- 灵活,但需要较多配置。
- 适合自己造轮子。
4. qiankun:蚂蚁开箱即用的方案
基于single-spa,内置了JS沙箱、样式隔离、HTML Entry(自动加载子应用的HTML、JS、CSS)。你只需要改几行代码,就能把一个普通应用变成微前端子应用。
- 推荐大部分项目用qiankun。
- 支持Vue、React、Angular等。
5. Webpack 5 Module Federation:去中心化的“共享冰箱”
不需要主应用,任意两个应用可以互相暴露和使用模块。运行时动态加载对方代码,像从冰箱里拿菜一样。
- 非常适合多个独立部署的微前端应用。
- 需要Webpack 5支持。
四、qiankun 实战:三步把React应用变成子应用
假设你有一个主应用(基座),一个子应用(React)。
主应用(基座)注册子应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'reactApp',
entry: '//localhost:3001', // 子应用启动的地址
container: '#subapp-container',
activeRule: '/react',
},
]);
start();
子应用(React)改造
在src/index.js里暴露生命周期:
function render(props) {
ReactDOM.render(<App />, document.getElementById('root'));
}
if (!window.__POWERED_BY_QIANKUN__) {
render(); // 独立运行时直接渲染
}
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount() {
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
}
再改webpack配置,让打包成umd格式:
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
globalObject: 'window',
}
搞定!子应用独立运行时正常访问,被qiankun加载时也能完美嵌入。
五、JS沙箱:防止子应用污染全局
qiankun提供了两种沙箱:
- SnapshotSandbox:记录恢复window属性变化(兼容IE)。
- ProxySandbox:用ES6 Proxy代理对window的读写,每个子应用有自己的fakeWindow。
这样子应用里修改window、document都不会影响全局。
六、样式隔离:你的样式别弄脏我的衣服
qiankun默认使用shadowDOM(需要子应用支持),也可以通过配置strictStyleIsolation开启。或者简单约定:子应用所有样式加namespace。
七、应用间通信:传递“小纸条”
- 通过props传递:主应用mount子应用时,可以传入通信函数。
-
全局状态管理:用
qiankun的initGlobalState。 -
自定义事件:
window.dispatchEvent(但注意沙箱可能隔离window)。
八、常见坑点与建议
-
重复依赖:多个子应用都打包了React,体积大。解决方案:用
externals或Module Federation共享。 -
子应用间路由跳转:用
history.pushState前判断是否在微前端环境,调用主应用的路由实例。 - 公共样式:主应用提供全局样式,子应用只写局部样式。
-
性能:预加载子应用,或使用
loadable组件按需加载。
九、Module Federation:不用主应用的“分布式”微前端
如果你的项目没有明确的主应用,每个应用都可以暴露模块给其他应用,用Webpack 5的ModuleFederationPlugin。
// 应用A暴露组件
new ModuleFederationPlugin({
name: 'appA',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button',
},
});
// 应用B消费
new ModuleFederationPlugin({
name: 'appB',
remotes: {
appA: 'appA@http://localhost:3001/remoteEntry.js',
},
});
// 在B里异步加载:import('appA/Button')
这样两个应用独立部署,运行时动态加载对方组件,超级灵活。
十、总结:微前端不是银弹,但能救急
- 微前端适合超大项目、多团队、技术栈升级。
- 简单场景用
qiankun,复杂场景用Module Federation。 - 注意JS沙箱、样式隔离、通信成本。
- 如果项目只有几十个页面,别折腾,用组件化就够了。
微前端就像乐高积木:拆开是独立小玩具,拼起来是宏伟城堡。用得好,团队效率翻倍;用不好,调试到你怀疑人生。
如果你觉得今天的“乐高城堡”够形象,点个赞让更多人看到。明天我们将聊聊前端设计模式——单例、观察者、工厂、策略,那些让你代码更优雅的套路。我们明天见!