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 文件夹,专门放微前端相关的配置。这样结构清晰,后期也好找。

小提示: 我建议把主应用的代码和子应用的注册逻辑完全分离。主应用只负责 UI 框架和公共布局,子应用注册写在独立的模块里。这样哪怕以后要切换微前端框架,改动成本也低。

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 提供了 registerMicroAppsstart 两个 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 应用变成「可被加载」的微前端子应用。到时候我会分享一个我实际项目中遇到的「子应用生命周期混乱」的案例,挺有意思的。