跳到主要内容前端微前端架构实战:如何避免应用变成巨石 | 极客日志JavaScriptSaaS大前端
前端微前端架构实战:如何避免应用变成巨石
综述由AI生成前端微前端架构实践旨在解决单体应用维护难、构建慢及团队协作困难的问题。文章对比了 Module Federation、Single-SPA 和 Qiankun 三种主流技术方案,详细解析了各自的配置要点与适用场景。通过拆分巨石应用为独立微应用,可显著提升构建速度与迭代效率,适合中大型前端项目采用。
孤勇者16 浏览 前端微前端架构实战:如何避免应用变成巨石
痛点剖析
维护一个百万行代码的单体应用是什么体验?改个功能牵一发而动全身,构建时间动辄十几分钟,团队协作寸步难行。这就像住在一间没有分区的大房子里,虽然能住,但乱得让人头疼。
最近接手的项目代码量超过 100 万行,构建时间超过 10 分钟,这种效率在快速迭代的业务面前简直是灾难。我们是在做应用,还是在维护一个巨大的代码仓库?
反面教材:单体结构
import React from 'react';
import Header from './components/Header';
import Sidebar from './components/Sidebar';
import Dashboard from './components/Dashboard';
import Users from './components/Users';
import Products from './components/Products';
import Orders from './components/Orders';
import Settings from './components/Settings';
function App() {
return (
<div>
<Header />
<div>
<Sidebar />
<>
);
}
;
main
<Dashboard />
<Users />
<Products />
<Orders />
<Settings />
</main>
</div>
</div>
export
default
App
把所有组件都塞进一个大文件里,无论有用没用,全堆在一起。这种结构一旦膨胀,谁碰谁崩。
解决方案
1. Module Federation (Webpack 5)
这是目前最推荐的方案之一,利用 Webpack 5 的原生能力实现运行时模块共享。
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
dashboard: 'dashboard@http://localhost:3001/remoteEntry.js',
users: 'users@http://localhost:3002/remoteEntry.js',
products: 'products@http://localhost:3003/remoteEntry.js',
orders: 'orders@http://localhost:3004/remoteEntry.js',
settings: 'settings@http://localhost:3005/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};
这里的关键是 shared 字段,确保 React 等核心库只加载一份,避免重复消耗内存。远程应用只需暴露对应的组件即可。
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'dashboard',
filename: 'remoteEntry.js',
exposes: {
'./Dashboard': './src/Dashboard',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};
import React, { lazy, Suspense } from 'react';
import Header from './components/Header';
import Sidebar from './components/Sidebar';
const Dashboard = lazy(() => import('dashboard/Dashboard'));
const Users = lazy(() => import('users/Users'));
const Products = lazy(() => import('products/Products'));
const Orders = lazy(() => import('orders/Orders'));
const Settings = lazy(() => import('settings/Settings'));
function App() {
return (
<div>
<Header />
<div>
<Sidebar />
<main>
<Suspense fallback={<div>加载中...</div>}>
<Dashboard />
<Users />
<Products />
<Orders />
<Settings />
</Suspense>
</main>
</div>
</div>
);
}
export default App;
通过 lazy 和 Suspense,我们可以按需加载这些远程模块,显著减少首屏体积。
2. Single-SPA
如果你不想被 Webpack 绑定,Single-SPA 提供了更底层的框架无关支持。
import { registerApplication, start } from 'single-spa';
registerApplication({
name: '@my-org/dashboard',
app: () => import('@my-org/dashboard'),
activeWhen: (location) => location.pathname.startsWith('/dashboard'),
});
registerApplication({
name: '@my-org/users',
app: () => import('@my-org/users'),
activeWhen: (location) => location.pathname.startsWith('/users'),
});
start();
这里通过 URL 路径来激活不同的微应用,非常适合多路由场景。
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
import Dashboard from './Dashboard';
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Dashboard,
errorBoundary(err, info, props) {
return <div>应用出错了</div>;
},
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;
3. Qiankun
基于 Single-SPA 封装,更适合企业级开发,提供了沙箱隔离等开箱即用的功能。
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'dashboard',
entry: '//localhost:3001',
container: '#micro-app-container',
activeRule: '/dashboard',
},
{
name: 'users',
entry: '//localhost:3002',
container: '#micro-app-container',
activeRule: '/users',
},
]);
start();
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
let instance = null;
function render(props) {
const { container } = props;
instance = createApp(App);
instance.use(router);
instance.mount(container ? container.querySelector('#app') : '#app');
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('dashboard bootstraped');
}
export async function mount(props) {
console.log('dashboard mounted', props);
render(props);
}
export async function unmount() {
console.log('dashboard unmounted');
instance.unmount();
instance = null;
}
注意 __POWERED_BY_QIANKUN__ 全局变量的判断,这是防止样式冲突和重复渲染的关键。
核心建议
微前端不是银弹,但在特定规模下是必要的选择。将应用拆分成多个独立的微应用,团队协作更高效,构建时间更短,再也不用担心巨石应用的问题了。根据团队技术栈和业务复杂度,选择合适的方案落地即可。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online