Skip to main content

微前端路由跳转解析

一、微前端路由核心原理

在微前端架构中,路由系统承担着应用调度核心职责。qiankun 通过劫持路由事件实现应用加载控制,其核心机制包含:

  1. 路由监听层:通过覆写 window.historywindow.addEventListener (popstate / hashchange) 捕获路由变化;
  2. 应用匹配器:根据 activeRule 配置匹配当前路由对应的子应用;
  3. 沙箱隔离:为每个子应用创建独立的运行环境,包括路由上下文隔离。

二、主应用跳转子应用

下面以 cra 搭建的 react 主应用以及两个子应用为例,实现微前端路由跳转,项目结构参考:

main-app/
├─ packages/
│ ├─ apps/
│ │ ├─ app1-react/ # CRA React子应用
│ │ └─ app2-vue/ # Vite Vue子应用
└─ src/ # 主应用代码

主应用跳转子应用

// 使用 React Router 的 Link 组件
<Link to="/app1">跳转React子应用</Link>
<Link to="/app2">跳转Vue子应用</Link>
main-app/src/index.js
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
{
name: 'app1-react',
entry: '//localhost:3001',
container: '#subapp-container',
activeRule: '/app1',
},
{
name: 'app2-vue',
entry: '//localhost:3002',
container: '#subapp-container',
activeRule: '/app2',
}
]);

start();

三、子应用跳转主应用 & 子应用间跳转

子应用跳转主应用

// 使用 history API
window.history.pushState({}, '', '/');

子应用间跳转

// 跳转到 React 子应用
window.history.pushState({}, '', '/app1');

// 跳转到 Vue 子应用
window.history.pushState({}, '', '/app2');

1. 子应用 app1-react 代码

app1-react/src/index.js
import './public-path';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

let root = null;

function render(props = {}) {
const { container } = props;
const rootElement = container
? container.querySelector('#root')
: document.getElementById('root');

root = ReactDOM.createRoot(rootElement);
root.render(
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app1' : '/'}>
<App />
</BrowserRouter>
);
}

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}

// 子应用生命周期
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount(props) {
root.unmount();
}

2. 子应用 app2-vue 代码

app2-vue/src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

let app = null;

function render(props = {}) {
const { container } = props;
app = createApp(App);
app.use(router);
app.mount(container ? container.querySelector('#app') : '#app');
}

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}

// 微应用生命周期
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount() {
app.unmount();
}
注意
  1. 确保所有应用使用相同的路由模式(推荐全部使用 history 模式)
  2. 主应用需要配置 nginx 或代理处理子应用路由的 fallback
  3. 子应用需要支持独立运行和嵌入运行两种模式
  4. 跨应用跳转建议使用主应用提供的统一路由管理
  5. 开发时需要同时启动主应用和子应用:
main-app: 3000
app1-react: 3001
app2-vue: 3002

三、进阶路由处理技巧

1. 路由守卫集成

// 主应用全局守卫
router.beforeEach(async (to, from, next) => {
if (to.meta.isMicroApp) {
const app = getAppConfig(to.meta.appName);

// 检查是否已加载
if (!app.loaded) {
await loadMicroApp(app);
app.loaded = true;
}

// 传递路由状态
app.instance?.setRouteState?.(to.params);
}
next();
});

2. 动态路由注入

// 异步从接口获取子应用配置
async function initDynamicRoutes() {
const apps = await fetch('/api/micro-apps');

apps.forEach(app => {
router.addRoute({
path: `/${app.code}/*`,
component: MicroAppWrapper,
meta: {
isMicroApp: true,
appConfig: app
}
});
});
}

四、常见问题解决方案

1. 路由冲突处理

// 精确匹配主应用路由
registerMicroApps([
{
activeRule: (location) =>
location.pathname.startsWith("/main-layout/app1") &&
!location.pathname.includes("/main-layout/app1/exclude"),
},
]);

2. Hash 模式适配

// 主应用 qiankun 配置
start({
sandbox: { experimentalStyleIsolation: true },
useHashMode: true, // 启用 hash 路由模式
});

// 子应用打包配置
publicPath: process.env.NODE_ENV === "production"
? "/hash-path/#/"
: "//localhost:7100/#/";

3. 异常处理策略

// 全局错误捕获
router.onError((error) => {
if (error.message.includes('chunk')) {
showReloadDialog('检测到新版本,即将刷新页面');
setTimeout(() => location.reload(), 2000);
}
});

// 子应用降级处理
window.addEventListener('unhandledrejection', (e) => {
if (e.reason.message.includes('subapp')) {
router.push('/error?type=subapp_down');
}
});