4、主应用搭建:基于 React 的主应用框架搭建、路由配置、子应用注册与加载
好,咱们进入实战环节。
前面聊了那么多微前端的概念和架构选型,说白了,最终都要落到代码上。这一章,我们就来亲手搭一个主应用。我选 React 作为主框架,因为它在社区里最成熟,生态也最全。你如果习惯用 Vue,思路是完全一样的,只是语法换一换。
4.1 主应用框架搭建
我个人习惯用 Create React App 来初始化项目。虽然现在 Vite 很火,但 CRA 胜在稳定,团队里新同学上手也快。
npx create-react-app micro-main-app
cd micro-main-app
npm start
嗯,这里要注意:微前端的主应用,本质上就是一个「壳」。它不需要承载太多业务逻辑,核心职责是路由分发和子应用管理。所以,别把主应用写得太重,否则后面维护起来会头疼。
接下来,我们需要安装 qiankun。qiankun 是蚂蚁开源的微前端框架,基于 single-spa 封装,用起来非常顺手。
npm install qiankun --save
装完之后,在 src 目录下创建一个 micro 文件夹,专门放微前端相关的配置。这样结构清晰,后期也好找。
4.2 路由配置
路由这块,我踩过不少坑。最开始我天真地以为,主应用的路由和子应用的路由各管各的就行。结果一跑起来,页面刷新就 404,或者路由冲突,跳来跳去乱套了。
为什么会这样?因为主应用和子应用共享同一个浏览器 URL。你必须规划好路由的「命名空间」。说白了,就是给每个子应用分配一个独立的前缀。
比如,主应用的路由长这样:
// src/App.js
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<div>
<nav>
<Link to="/">首页</Link>
<Link to="/app1">子应用1</Link>
<Link to="/app2">子应用2</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/app1/*" element={<MicroAppContainer />} />
<Route path="/app2/*" element={<MicroAppContainer />} />
</Routes>
</div>
</BrowserRouter>
);
}
注意看 /app1/* 这里的星号。它表示匹配 /app1 下的所有子路径。子应用内部的路由,比如 /app1/detail,就由子应用自己处理。主应用只负责把流量引到对应的容器里。
4.3 子应用注册与加载
子应用注册,是微前端最核心的一步。qiankun 提供了 registerMicroApps 和 start 两个 API,用起来很简洁。
我在 src/micro 下新建一个 apps.js 文件,专门放子应用的配置:
// src/micro/apps.js
const apps = [
{
name: 'app1', // 子应用名称,唯一
entry: '//localhost:3001', // 子应用入口地址
container: '#micro-container',// 挂载点
activeRule: '/app1', // 激活规则
},
{
name: 'app2',
entry: '//localhost:3002',
container: '#micro-container',
activeRule: '/app2',
},
];
export default apps;
然后在 src/micro/index.js 里完成注册和启动:
// src/micro/index.js
import { registerMicroApps, start } from 'qiankun';
import apps from './apps';
export function setupMicroApps() {
registerMicroApps(apps, {
beforeLoad: [async app => console.log('before load', app.name)],
beforeMount: [async app => console.log('before mount', app.name)],
afterMount: [async app => console.log('after mount', app.name)],
beforeUnmount: [async app => console.log('before unmount', app.name)],
afterUnmount: [async app => console.log('after unmount', app.name)],
});
start({
sandbox: { experimentalStyleIsolation: true }, // 开启样式隔离
});
}
最后,在主应用的入口文件里调用它:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { setupMicroApps } from './micro';
setupMicroApps(); // 注册子应用
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
你看,核心代码就这么几行。但这里有几个细节,我当年可是吃了不少苦头。
4.4 避坑与最佳实践
| 常见问题 | 原因 | 解决方案 |
|---|---|---|
| 子应用加载后样式错乱 | 主应用和子应用的 CSS 互相影响 | 开启 experimentalStyleIsolation,或者子应用使用 CSS Modules |
| 子应用内部路由跳转后主应用白屏 | 主应用路由没有匹配到子应用前缀 | 检查 activeRule 是否与子应用路由前缀一致 |
| 子应用加载两次 | 主应用重复调用了 registerMicroApps |
确保只调用一次,或者在 useEffect 里加空依赖 |
| 子应用全局变量污染 | 子应用修改了 window 对象 |
开启 qiankun 的沙箱模式(默认开启) |
嗯,这一章的内容就到这里。下一章我们会深入子应用的改造,看看如何让一个普通的 React 应用变成「可被加载」的微前端子应用。到时候我会分享一个我实际项目中遇到的「子应用生命周期混乱」的案例,挺有意思的。