4-7 Umi&qiankun微前端实践
微前端的核心概念
微前端是一种将大型单体前端应用拆分为多个小型、独立可交付的子应用的架构风格。每个子应用可以独立开发、独立部署、独立运行,但在用户看来是一个完整的应用。
qiankun 是蚂蚁集团基于 single-spa 封装的微前端框架,提供了开箱即用的微前端能力:
| 能力 | 说明 |
|---|---|
| 样式隔离 | 子应用之间样式互不污染 |
| JS 沙箱 | 子应用的全局变量互不影响 |
| 独立部署 | 每个子应用可以独立构建和部署 |
| 技术栈无关 | 子应用可以是 React、Vue、Angular 等任意框架 |
| 资源预加载 | 在浏览器空闲时间预加载子应用资源 |
主应用配置
安装与配置
// 主应用 .umirc.ts
export default {
qiankun: {
master: {
apps: [
{
name: 'sub-react',
entry: process.env.NODE_ENV === 'development'
? '//localhost:8001'
: '/sub-react/',
props: { mainAppToken: 'xxx' }, // 传递给子应用的数据
},
{
name: 'sub-vue',
entry: '//localhost:8002',
},
],
},
},
};
typescript
路由绑定子应用
// .umirc.ts
export default {
routes: [
{ path: '/', component: 'index' },
{ path: '/sub-react', microApp: 'sub-react' },
{ path: '/sub-vue', microApp: 'sub-vue' },
],
};
typescript
使用 MicroApp 组件
// src/pages/Dashboard.tsx
import { MicroApp, useQiankunStateForSlave } from 'umi';
export default function Dashboard() {
return (
<div>
<h1>主应用 Dashboard</h1>
<MicroApp
name="sub-react"
loader={(loading) => loading && <div>加载中...</div>}
/>
</div>
);
}
tsx
主子应用通信
// 主应用发送数据给子应用
import { useModel } from 'umi';
export default function Layout() {
const { setQiankunGlobalState } = useModel('@@qiankunState');
const handleLogin = (token: string) => {
setQiankunGlobalState({ token, user: { name: 'Admin' } });
};
return <button onClick={() => handleLogin('abc')}>登录</button>;
}
tsx
子应用配置
React 子应用(Umi)
// 子应用 .umirc.ts
export default {
qiankun: {
slave: {},
},
// 子应用需要配置跨域
headers: {
'Access-Control-Allow-Origin': '*',
},
};
typescript
// 子应用 src/app.ts
export const qiankun = {
// 子应用生命周期钩子
async mount(props) {
console.log('子应用挂载', props);
// 接收主应用传递的数据
props.onGlobalStateChange?.((state) => {
console.log('主应用状态变化:', state);
});
},
async unmount() {
console.log('子应用卸载');
},
};
typescript
非 Umi 子应用适配
对于非 Umi 的子应用(如 Vue),需要手动导出生命周期钩子:
// Vue 子应用的 main.js
import { createApp } from 'vue';
import App from './App.vue';
let app = null;
function render(props = {}) {
const { container } = props;
app = createApp(App);
app.mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// qiankun 生命周期
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount() {
app?.unmount();
}
javascript
开发环境配置
各应用需要在不同的端口启动:
# 主应用:8000 端口
cd main-app && umi dev
# React 子应用:8001 端口
cd sub-react && umi dev
# Vue 子应用:8002 端口
cd sub-vue && npm run dev
bash
Webpack 配置中需要允许跨域:
// 子应用的 webpack 配置
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
output: {
library: 'sub-react',
libraryTarget: 'umd',
},
javascript
生产环境部署
| 方案 | 说明 |
|---|---|
| 统一域名 + Nginx | 主应用部署在根路径,子应用部署在 /sub-xxx/ 子路径 |
| 不同子域名 | 每个子应用使用独立的子域名 |
| CDN 部署 | 子应用的静态资源上传到 CDN |
Nginx 配置示例:
server {
listen 80;
server_name app.example.com;
# 主应用
location / {
root /usr/share/nginx/main-app;
try_files $uri $uri/ /index.html;
}
# React 子应用
location /sub-react/ {
root /usr/share/nginx/sub-react;
try_files $uri $uri/ /sub-react/index.html;
}
}
nginx
微前端适用场景
| 场景 | 是否适合微前端 |
|---|---|
| 大型企业管理系统(多团队协作) | 适合 |
| 需要渐进式迁移老旧系统 | 适合 |
| 不同业务线需要独立部署 | 适合 |
| 简单的单页面应用 | 不适合(增加复杂度) |
| 团队规模小于5人 | 不适合(过度设计) |
↑