qiankun
qiankun copied to clipboard
[Bug]主应用和子应用使用不同版本的vue后路由切换报错
What happens?
当子应用切换过一次路由后,主应用切换路由会报错
最小可复现仓库
https://gitee.com/baizhua/qiankun-vue3-demo.git
复现步骤,错误日志以及相关配置
操作步骤
1.点击子应用对应的路由(app-sub-vue2)
2.点击子应用的其他路由(About)
3.点击主应用其他路由(Home)
经过自己检查,错误是主应用的vue-router报出来的。 问题是主应用获取history.state信息的时候,由于子应用切换路由时修改到了history.state并且数据结构和主应用的vue-router所期望的不一致(vue-router3x和4x设置history.state的数据结构是不一样的)导致的
现在的解决方案是在主应用监听router.beforeEach手动修改history.state数据结构,希望能得到更好的解决方案。
相关环境信息
- 主应用信息
- [email protected]、[email protected]
- 子应用信息
- [email protected]、[email protected]
- qiankun 版本
- 2.4.0
- 浏览器版本:
- chrome 89
- 操作系统: macOS Big Sur
牛啊,老板
赞,所以vue-router的版本最好统一,这个坑我也踩了
多个版本 vue-router
主要导致2个问题:
Q. 子路由会监听路由变化,然后发出重复的路由跳转,导致主路由被动的响应
A. 修改子路由的 vue-router
,在判断到不属于自己的路由变化时,阻止路由跳转行为
Q. 低版本的 vue-router
在 pushState 的时候,会覆盖丢失主路由的 history.state
,导致主路由跳转异常
A. 修改子路由的 vue-router
,在 pushState 的时候,把当前的 history.state
传递回去
以上的解决办法均需要 fork vue-router
,并且自己维护,最好的还是通通采用一个版本的路由组件,目前那种情况是没有问题的
需要修改的文件:vue-router/src/util/push-state.js
export function pushState (url?: string, replace?: boolean) {
// 在程序里面定义一个子路由的规则,如果不属于子路由的变化,return 回去
if (url.indexOf(window.__QIANKUN_ROUTE_PATH__) < 0) return false
saveScrollPosition()
// try...catch the pushState call to get around Safari
// DOM Exception 18 where it limits to 100 pushState calls
const history = window.history
try {
const stateCopy = extend({}, history.state)
if (replace) {
// preserve existing history state as it could be overriden by the user
stateCopy.key = getStateKey()
history.replaceState(stateCopy, '', url)
} else {
// 携带上浏览器的 history.state,房主主路由丢失
history.pushState(Object.assign({}, stateCopy, { key: setStateKey(genStateKey()) }), '', url)
}
} catch (e) {
window.location[replace ? 'replace' : 'assign'](url)
}
}
能问一下,您是怎么设置history.state的吗? 那如果是需要保持vue-router保持一致,是该怎么去兼容了?
router.beforeEach((to, from, next) => {
if (_.isEmpty(history.state.current)) {
_.assign(history.state, { current: from.fullPath });
}
next();
});
问题解决了,感谢大佬🙏 @dongchaoge
@dongchaoge 感谢大佬
import _ from "lodash"; const windowHistory = window.history; router.beforeEach((to, from, next) => { if (_.isEmpty(windowHistory.state.current)) { _.assign(windowHistory.state, { current: from.fullPath }); } next(); });
我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4
子应用配置了 base,使用路由匹配模式激活子应用
在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载
router.beforeEach((to, from, next) => { if (_.isEmpty(history.state.current)) { _.assign(history.state, { current: from.fullPath }); } next(); });
问题解决了,感谢大佬🙏 @dongchaoge
请问下,这段代码加再哪个文件里面
多个版本
vue-router
主要导致2个问题: Q. 子路由会监听路由变化,然后发出重复的路由跳转,导致主路由被动的响应 A. 修改子路由的vue-router
,在判断到不属于自己的路由变化时,阻止路由跳转行为Q. 低版本的
vue-router
在 pushState 的时候,会覆盖丢失主路由的history.state
,导致主路由跳转异常 A. 修改子路由的vue-router
,在 pushState 的时候,把当前的history.state
传递回去以上的解决办法均需要 fork
vue-router
,并且自己维护,最好的还是通通采用一个版本的路由组件,目前那种情况是没有问题的需要修改的文件:
vue-router/src/util/push-state.js
export function pushState (url?: string, replace?: boolean) { // 在程序里面定义一个子路由的规则,如果不属于子路由的变化,return 回去 if (url.indexOf(window.__QIANKUN_ROUTE_PATH__) < 0) return false saveScrollPosition() // try...catch the pushState call to get around Safari // DOM Exception 18 where it limits to 100 pushState calls const history = window.history try { const stateCopy = extend({}, history.state) if (replace) { // preserve existing history state as it could be overriden by the user stateCopy.key = getStateKey() history.replaceState(stateCopy, '', url) } else { // 携带上浏览器的 history.state,房主主路由丢失 history.pushState(Object.assign({}, stateCopy, { key: setStateKey(genStateKey()) }), '', url) } } catch (e) { window.location[replace ? 'replace' : 'assign'](url) } }
感谢大佬
我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4
子应用配置了 base,使用路由匹配模式激活子应用
在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载
这个似乎是vue-router4的问题,有找到什么解决方案么?
Since the issue was labeled with out-of-scope
, but no response in 30 days. This issue will be close. If you have any questions, you can comment and reply.
由于该 issue 被标记为与本项目无关,却 30 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4 子应用配置了 base,使用路由匹配模式激活子应用 在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载
这个似乎是vue-router4的问题,有找到什么解决方案么?
解决了,用上面大佬说的方案改下低版本的 pushState 就行
我参考上面的方法并没有解决我的问题,在 beforeEach 不生效,因为 state 是在它之后修改的,然后使用另一种方法,劫持 push 方法,重新手动设置 history.state 的值
/**
*
* 为了作为微应用嵌入 qiankun,做兼容性处理;
* 解决主应用 使用 4.x 版本,微应用使用 3.x 版本 history.state 的参数结构不一致,导致导航报错问题
*/
export function routerCompatibilityProcessing() {
// 劫持 push 和 replace 方法,在回调函数中手动给 history.state 添加 current 参数
const onComplete = route => {
const base = '/apm/#'; // 主应用中对应该微应用的 base
Object.assign(history.state, {
// back: base + routerBackPath,
current: base + route.fullPath,
});
};
const push = router.push; // this.history.push(location, onComplete, onAbort);
const replace = router.replace;
router.push = location => {
push.call(router, location, onComplete);
};
router.replace = location => {
replace.call(router, location, onComplete);
};
}
我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4 子应用配置了 base,使用路由匹配模式激活子应用 在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载
这个似乎是vue-router4的问题,有找到什么解决方案么?
解决了,用上面大佬说的方案改下低版本的 pushState 就行
还是不太行(主应用 v3 路由,微应用使用 v4),当 url 包含非 ascii 字符时,v3 版本似乎会把它们转换为 url 编码格式 (encodeURIComponent
),v4 则不会。
以空格为例,在每个应用单独运行的情况下,url querystring 中如果有空格,在 v3 下为:?query=foo%20bar
,v4 下为:?query=foo+bar
微前端模式下为 ?query=foo%20bar
,这时候如果在微应用中触发路由跳转,还是会跳转 undefined。可能是不同版本之间对非 ascii 字符的处理存在差异导致的
我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4 子应用配置了 base,使用路由匹配模式激活子应用 在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载
这个似乎是vue-router4的问题,有找到什么解决方案么?
解决了,用上面大佬说的方案改下低版本的 pushState 就行
还是不太行(主应用 v3 路由,微应用使用 v4),当 url 包含非 ascii 字符时,v3 版本似乎会把它们转换为 url 编码格式 (
encodeURIComponent
),v4 则不会。以空格为例,在每个应用单独运行的情况下,url querystring 中如果有空格,在 v3 下为:
?query=foo%20bar
,v4 下为:?query=foo+bar
微前端模式下为
?query=foo%20bar
,这时候如果在微应用中触发路由跳转,还是会跳转 undefined。可能是不同版本之间对非 ascii 字符的处理存在差异导致的
请问最后解决了吗,同样是主应用v3,微应用v4。子应用内路由切换会重载子应用,并且浏览器回退会无法匹配到路由