如何实践解决 single-spa 微前端项目中 Vue 与 React 路由切换难题?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1349个文字,预计阅读时间需要6分钟。
介绍微前端+single-spa项目过程中,遇到的Vue子应用和React子应用路由跳转问题,及解决方案。
前言:本文主要介绍在微前端+single-spa项目过程中,遇到的Vue子应用和React子应用路由跳转问题,及解决方案。
在微前端项目中,我们通常会遇到将不同的应用作为子应用集成到主应用中。在这个过程中,Vue子应用和React子应用的路由跳转问题是一个常见的问题。以下是在实际项目中遇到的问题及解决方案:
1. 问题:Vue子应用和React子应用路由跳转失败
在微前端架构中,主应用负责路由管理,而Vue子应用和React子应用的路由配置在各自的子应用中。当主应用通过single-spa机制加载子应用时,可能会出现路由跳转失败的情况。
2. 解决方案:
a. 确保子应用的路由配置正确,并且路由路径与主应用的路由配置一致。
b. 在子应用的路由配置中,添加`name`属性,并确保`name`属性的值与主应用的路由配置一致。
c. 在主应用的路由配置中,使用`single-spa navigate`方法进行路由跳转,而不是直接使用`window.location.href`或`window.history.pushState`。
以下是Vue子应用和React子应用路由配置的示例:
Vue子应用路由配置: javascript const routes=[ { path: '/vue-app', name: 'vue-app', component: ()=> import('@/views/VueApp.vue') } ];
React子应用路由配置: javascript const routes=[ { path: '/react-app', name: 'react-app', component: React.lazy(()=> import('@/views/ReactApp')) } ];
主应用路由配置: javascript singleSpa.navigateToUrl({ path: '/vue-app' }); singleSpa.navigateToUrl({ path: '/react-app' });
通过以上方法,可以有效解决微前端项目中Vue子应用和React子应用的路由跳转问题。
介绍微前端 single-spa 项目过程中,遇到的 Vue 子应用和 React 子应用路由跳转问题,及实际项目中的解决方案。 前言本文介绍的是在做微前端 single-spa 项目过程中,遇到的 Vue 子应用和 React 子应用互相跳转路由时遇到的问题。
项目情况:single-spa 项目,基座用的是 React,目前是2个子应用一个 Vue 一个 React。路由方案是 Vue Router,React Router + history。
有交互场景是从 Vue 子应用跳转到 React 子应用,或者从 React 子应用跳转到 Vue 子应用,因此遇到了问题。
遇到的问题结合项目诉求和遇到的问题,大家可以先思考一下有什么解决方案~
------------------------------分割线------------------------------
解决的方案主要是要处理好以下2个因素:
- 正常触发当前页面的路由钩子
- 正常传路由参数到目的页面
我一开始尝试去查阅社区文章和看部分源码,看是否有什么特殊方式自主去触发路由钩子,等钩子处理完成之后再跳转去目的页面(跳去 Vue 用 Vue Router,跳去 React 用 React Router)
但看源码下来发现,想要触发 Prompt 还是需要调用 history.push 触发流程,想要触发 Vue Router 导航守卫还是需要调用 VueRouter.push 触发流程
所以结合这两点我整出了解决方案,已使用在项目当中,下面是封装的全局路由跳转工具:
window.micro = {
// 子应用,会在相应应用挂载完成后设置
apps: { vue: null, react: null },
history: {
push: (location, onComplete, onAbort) => {
const url = typeof location === 'string' ? location : location.path;
// 判断是哪个子应用
const currentIsReact = isReactApp();
const nextIsReact = isReactApp(`#${url}`);
// 处理路由参数
let state = {};
let query = {};
let name = '';
if (typeof location !== 'string') {
state = location.params || {};
query = location.query || {};
name = location.name || '';
}
if (!currentIsReact && nextIsReact) {
// vue 跳 react:先用 vue-router 跳,在跳完的回调里再用 history 跳
const reactHistoryHandle = () => {
onComplete?.();
history.push(`#/temp?t=${Math.random()}`);
history.replace({ state, pathname: url, search: setQueryStringArgs(query) });
// 因为跳多了1次 vue-router,所以 back 一下
window.micro.apps.vue2.$router.back();
};
window.micro.apps.vue.$router.push(name ? { name, params: state, query } : { path: url, query }, reactHistoryHandle, onAbort);
} else if (currentIsReact && !nextIsReact) {
// react 跳 vue:先用 history 跳临时路由,再用 vue-router 跳,要配合 history.listen 做处理
react2vue = () => {
window.micro.apps.vue.$router.push(name ? { name, params: state, query } : { path: url, query }, onComplete, onAbort);
};
history.push('/temp_react_to_vue');
} else if (currentIsReact && nextIsReact) {
// react 跳 react:没有特殊,正常用 history 跳
} else {
// vue 跳 vue:没有特殊,正常用 vue-router 跳
}
},
},
};
配合的监听和工具函数:
// 处理 react 跳 vue的情况
let react2vue = null;
history.listen((location, action) => {
// 处理在临时路由的下一个路由,要返回上一个路由时,需要跳过临时路由
if (location.pathname === '/temp_react_to_vue' && action === 'POP') {
history.goBack();
} else if (location.pathname === '/temp_react_to_vue') {
// 在这里跳去真实的 vue-router 路由
react2vue?.();
react2vue = null;
}
});
// 工具函数
function isReactApp(hash = location.hash) {
// 实际根据自己微服务项目的子应用情况判断
return hash === '' || hash === '#/'
|| ['#/list', '#/read/', '#/compose', '#/login'].some(router => hash.startsWith(router))
;
}
// 把query参数变成query字符串,如 {a:1, b:2} 返回 ?a=1&b=2
function setQueryStringArgs(args) {
let str = '';
args = args || {};
for (const [key, value] of Object.entries(args)) {
str += `${key}=${encodeURIComponent(String(value))}`;
}
return str ? `?${str}` : '';
}
总结
这是我在实际项目中使用的方案,如有更优雅更好的方案,希望在评论区和我讨论~
收获 / 小彩蛋因为之前已经对 Vue Router 原理和源码比较熟悉了,所以这次借着这个问题,主要是去了解了 React Router 的 Prompt 组件的实现,这里简单总结一下:
本文共计1349个文字,预计阅读时间需要6分钟。
介绍微前端+single-spa项目过程中,遇到的Vue子应用和React子应用路由跳转问题,及解决方案。
前言:本文主要介绍在微前端+single-spa项目过程中,遇到的Vue子应用和React子应用路由跳转问题,及解决方案。
在微前端项目中,我们通常会遇到将不同的应用作为子应用集成到主应用中。在这个过程中,Vue子应用和React子应用的路由跳转问题是一个常见的问题。以下是在实际项目中遇到的问题及解决方案:
1. 问题:Vue子应用和React子应用路由跳转失败
在微前端架构中,主应用负责路由管理,而Vue子应用和React子应用的路由配置在各自的子应用中。当主应用通过single-spa机制加载子应用时,可能会出现路由跳转失败的情况。
2. 解决方案:
a. 确保子应用的路由配置正确,并且路由路径与主应用的路由配置一致。
b. 在子应用的路由配置中,添加`name`属性,并确保`name`属性的值与主应用的路由配置一致。
c. 在主应用的路由配置中,使用`single-spa navigate`方法进行路由跳转,而不是直接使用`window.location.href`或`window.history.pushState`。
以下是Vue子应用和React子应用路由配置的示例:
Vue子应用路由配置: javascript const routes=[ { path: '/vue-app', name: 'vue-app', component: ()=> import('@/views/VueApp.vue') } ];
React子应用路由配置: javascript const routes=[ { path: '/react-app', name: 'react-app', component: React.lazy(()=> import('@/views/ReactApp')) } ];
主应用路由配置: javascript singleSpa.navigateToUrl({ path: '/vue-app' }); singleSpa.navigateToUrl({ path: '/react-app' });
通过以上方法,可以有效解决微前端项目中Vue子应用和React子应用的路由跳转问题。
介绍微前端 single-spa 项目过程中,遇到的 Vue 子应用和 React 子应用路由跳转问题,及实际项目中的解决方案。 前言本文介绍的是在做微前端 single-spa 项目过程中,遇到的 Vue 子应用和 React 子应用互相跳转路由时遇到的问题。
项目情况:single-spa 项目,基座用的是 React,目前是2个子应用一个 Vue 一个 React。路由方案是 Vue Router,React Router + history。
有交互场景是从 Vue 子应用跳转到 React 子应用,或者从 React 子应用跳转到 Vue 子应用,因此遇到了问题。
遇到的问题结合项目诉求和遇到的问题,大家可以先思考一下有什么解决方案~
------------------------------分割线------------------------------
解决的方案主要是要处理好以下2个因素:
- 正常触发当前页面的路由钩子
- 正常传路由参数到目的页面
我一开始尝试去查阅社区文章和看部分源码,看是否有什么特殊方式自主去触发路由钩子,等钩子处理完成之后再跳转去目的页面(跳去 Vue 用 Vue Router,跳去 React 用 React Router)
但看源码下来发现,想要触发 Prompt 还是需要调用 history.push 触发流程,想要触发 Vue Router 导航守卫还是需要调用 VueRouter.push 触发流程
所以结合这两点我整出了解决方案,已使用在项目当中,下面是封装的全局路由跳转工具:
window.micro = {
// 子应用,会在相应应用挂载完成后设置
apps: { vue: null, react: null },
history: {
push: (location, onComplete, onAbort) => {
const url = typeof location === 'string' ? location : location.path;
// 判断是哪个子应用
const currentIsReact = isReactApp();
const nextIsReact = isReactApp(`#${url}`);
// 处理路由参数
let state = {};
let query = {};
let name = '';
if (typeof location !== 'string') {
state = location.params || {};
query = location.query || {};
name = location.name || '';
}
if (!currentIsReact && nextIsReact) {
// vue 跳 react:先用 vue-router 跳,在跳完的回调里再用 history 跳
const reactHistoryHandle = () => {
onComplete?.();
history.push(`#/temp?t=${Math.random()}`);
history.replace({ state, pathname: url, search: setQueryStringArgs(query) });
// 因为跳多了1次 vue-router,所以 back 一下
window.micro.apps.vue2.$router.back();
};
window.micro.apps.vue.$router.push(name ? { name, params: state, query } : { path: url, query }, reactHistoryHandle, onAbort);
} else if (currentIsReact && !nextIsReact) {
// react 跳 vue:先用 history 跳临时路由,再用 vue-router 跳,要配合 history.listen 做处理
react2vue = () => {
window.micro.apps.vue.$router.push(name ? { name, params: state, query } : { path: url, query }, onComplete, onAbort);
};
history.push('/temp_react_to_vue');
} else if (currentIsReact && nextIsReact) {
// react 跳 react:没有特殊,正常用 history 跳
} else {
// vue 跳 vue:没有特殊,正常用 vue-router 跳
}
},
},
};
配合的监听和工具函数:
// 处理 react 跳 vue的情况
let react2vue = null;
history.listen((location, action) => {
// 处理在临时路由的下一个路由,要返回上一个路由时,需要跳过临时路由
if (location.pathname === '/temp_react_to_vue' && action === 'POP') {
history.goBack();
} else if (location.pathname === '/temp_react_to_vue') {
// 在这里跳去真实的 vue-router 路由
react2vue?.();
react2vue = null;
}
});
// 工具函数
function isReactApp(hash = location.hash) {
// 实际根据自己微服务项目的子应用情况判断
return hash === '' || hash === '#/'
|| ['#/list', '#/read/', '#/compose', '#/login'].some(router => hash.startsWith(router))
;
}
// 把query参数变成query字符串,如 {a:1, b:2} 返回 ?a=1&b=2
function setQueryStringArgs(args) {
let str = '';
args = args || {};
for (const [key, value] of Object.entries(args)) {
str += `${key}=${encodeURIComponent(String(value))}`;
}
return str ? `?${str}` : '';
}
总结
这是我在实际项目中使用的方案,如有更优雅更好的方案,希望在评论区和我讨论~
收获 / 小彩蛋因为之前已经对 Vue Router 原理和源码比较熟悉了,所以这次借着这个问题,主要是去了解了 React Router 的 Prompt 组件的实现,这里简单总结一下:

