微前端中 History 模式的路由拦截 ,和传统单应用前端路由拦截 (比如 Vue Router 的 beforeEach、React Router 的 useNavigate 拦截)在核心目标、实现逻辑、处理范围等方面的本质区别,我会从「本质定位→核心维度对比→实战案例→关键总结」层层拆解,结合你熟悉的 Vue/Wujie 场景让差异一目了然。
一、先厘清:两者的核心本质定位
1. 传统前端路由拦截(单应用)
是单应用内部的 "路由管控" :拦截自身应用的路由跳转(如 /proOrder→/home),目的是在跳转前 / 后做 "应用内逻辑处理"(鉴权、页面标题修改、加载动画),不改变路由的底层执行逻辑,只是 "加一层过滤"。
- 示例:Vue Router 的
beforeEach守卫,拦截后要么next()放行,要么next('/login')重定向,路由的最终执行还是浏览器原生 History API。
2. 微前端 History 路由拦截
是多应用(主 + 子)的 "路由沙箱劫持" :不仅拦截,还改写路由的底层执行逻辑 (重写 history.pushState、劫持 popstate 事件),目的是实现 "主 - 子应用路由隔离 + 全局历史记录统一",让多个子应用的路由看起来像 "单应用"。
- 示例:Wujie 拦截子应用的
pushState('/proOrder'),自动拼接前缀变成/vue3-app/proOrder,子应用感知不到,但全局 URL 已被改写。
二、核心差异对比(6 个维度)
| 对比维度 | 传统单应用路由拦截(History 模式) | 微前端 History 模式路由拦截 |
|---|---|---|
| 核心目标 | 单应用内的路由管控(鉴权、跳转控制、埋点) | 多应用路由隔离 + 全局历史记录统一 + 子应用无感知适配 |
| 拦截范围 | 仅拦截当前应用自身的路由操作 | 拦截「主应用 + 所有子应用」的全局路由操作 |
| 实现方式 | 基于框架路由守卫(如 Vue Router beforeEach),仅 "监听 / 过滤",不修改原生 API |
重写浏览器原生 history API(pushState/replaceState)+ 劫持 popstate 事件,改写底层逻辑 |
| 路由处理逻辑 | 路由路径是 "应用内相对路径"(如 /proOrder),直接使用 |
路由路径分 "全局路径"(/vue3-app/proOrder)和 "子应用内部路径"(/proOrder),需拼接 / 剥离前缀 |
| 历史记录管理 | 仅维护自身应用的历史记录栈 | 维护「全局统一的历史记录栈」,关联主 / 子应用的路由变化 |
| 事件处理 | 监听自身的 popstate 事件,无冲突 |
劫持所有子应用的 popstate 监听,包装后执行(避免事件冒泡 / 冲突) |
| 框架依赖 | 依赖单应用路由框架(Vue Router/React Router) | 依赖微前端框架(Wujie/Qiankun)的 "路由沙箱" 能力 |
| 侵入性 | 对应用无侵入(仅加守卫) | 对子应用有轻量侵入(需适配路由监听,如禁用 history.listen) |
三、实战案例对比(更直观)
案例 1:传统 Vue 单应用路由拦截(你的 Vue3 子应用原生逻辑)
typescript
// 传统单应用(Vue3)的路由拦截:仅管控,不改写底层
router.beforeEach((to, from, next) => {
// 目标:鉴权、改标题
document.title = to.meta.title;
if (to.meta.requiresAuth && !localStorage.getItem('token')) {
next('/login'); // 拦截后重定向,路由底层逻辑不变
} else {
next(); // 放行,原生 pushState 执行
}
});
// 点击跳转按钮时,执行原生 pushState,URL 直接变成 /proOrder
document.querySelector('#toProOrder').onclick = () => {
history.pushState({}, '', '/proOrder'); // 原生 API,无改写
};
关键:拦截只做 "过滤 / 重定向",history.pushState 还是浏览器原生方法,URL 就是子应用自己的路径,无拼接。
案例 2:微前端 Wujie 路由拦截(你的 Vue2 主应用 + Vue3 子应用)
javascript
// 1. 主应用:Wujie 重写子应用的 pushState(核心改写)
const rawPushState = window.history.pushState;
window.history.pushState = function(state, title, url) {
// 改写逻辑:拼接子应用前缀 /vue3-app
const fullUrl = '/vue3-app' + url;
return rawPushState.call(window.history, state, title, fullUrl);
};
// 2. 子应用点击跳转按钮时,看似调用原生 API,实则执行改写后的逻辑
document.querySelector('#toProOrder').onclick = () => {
history.pushState({}, '', '/proOrder'); // 子应用感知不到改写
// 实际全局 URL 变成:/vue3-app/proOrder(而非子应用以为的 /proOrder)
};
// 3. 主应用路由守卫:拦截全局 URL,识别子应用
router.beforeEach((to, from, next) => {
if (to.path.startsWith('/vue3-app')) {
loadVue3SubApp(); // 加载子应用
const subPath = to.path.replace('/vue3-app', ''); // 剥离前缀得到 /proOrder
setSubAppRoute(subPath); // 告诉子应用要渲染的路由
next();
}
});
关键:子应用调用的 pushState 是被改写后的方法,URL 被拼接前缀;主应用再通过前缀识别子应用,实现 "隔离 + 统一"。
四、关键差异的底层原因
- 场景复杂度不同 :
- 传统单应用:只有一套路由规则,无需考虑 "多应用冲突",拦截只需 "管控";
- 微前端:多应用共享同一个浏览器
history对象,必须通过 "劫持 + 改写" 实现隔离,否则子应用的路由会覆盖主应用。
- 路由归属不同 :
- 传统单应用:路由是 "应用私有" 的,URL 直接对应自身组件;
- 微前端:路由是 "全局共享" 的,URL 需包含 "子应用标识 + 子应用路由",因此必须拼接 / 剥离前缀。
- 历史记录诉求不同 :
- 传统单应用:只需维护自身的历史记录,回退 / 前进只影响自身;
- 微前端:需让用户感知不到 "多应用",因此要统一管理全局历史记录,回退时需同时同步主 / 子应用的路由状态。
总结(核心关键点)
- 本质区别:传统拦截是 "单应用内的路由管控(过滤 / 重定向)",不改写原生 API;微前端拦截是 "多应用的路由沙箱劫持",改写原生 History API 实现隔离;
- 核心差异:微前端需要处理 "全局路径 + 子应用内部路径" 的转换(前缀拼接 / 剥离),而传统拦截只有 "应用内路径";
- 最终效果:传统拦截不改变 URL 的底层格式,微前端拦截让子应用的路由 "看似独立,实则被主应用统一管理";
- 结合你的场景:你在 Wujie 中配置的
prefix="/vue3-app",就是微前端拦截和传统拦截的核心区别体现 ------ 传统路由不需要这个前缀,微前端必须靠它实现隔离。