qiankun icon indicating copy to clipboard operation
qiankun copied to clipboard

[Bug]主应用和子应用使用不同版本的vue后路由切换报错

Open keithMonster opened this issue 3 years ago • 10 comments

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数据结构,希望能得到更好的解决方案。

相关环境信息

keithMonster avatar Apr 02 '21 02:04 keithMonster

牛啊,老板

aasdsad avatar Apr 13 '21 11:04 aasdsad

赞,所以vue-router的版本最好统一,这个坑我也踩了

jianghao123ok avatar Apr 14 '21 09:04 jianghao123ok

多个版本 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)
  }
}

BelinChung avatar Apr 15 '21 11:04 BelinChung

能问一下,您是怎么设置history.state的吗? 那如果是需要保持vue-router保持一致,是该怎么去兼容了?

changlaosan avatar Jun 07 '21 04:06 changlaosan

image

router.beforeEach((to, from, next) => {
  if (_.isEmpty(history.state.current)) {
    _.assign(history.state, { current: from.fullPath });
  }
  next();
});

问题解决了,感谢大佬🙏 @dongchaoge

EraChen233 avatar Aug 24 '21 13:08 EraChen233

@dongchaoge 感谢大佬

airoy avatar Oct 19 '21 10:10 airoy

import _ from "lodash"; const windowHistory = window.history; router.beforeEach((to, from, next) => { if (_.isEmpty(windowHistory.state.current)) { _.assign(windowHistory.state, { current: from.fullPath }); } next(); });

chenjidu avatar Feb 10 '22 09:02 chenjidu

我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4

子应用配置了 base,使用路由匹配模式激活子应用

在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载

youthug avatar Jun 29 '22 11:06 youthug

image

router.beforeEach((to, from, next) => {
  if (_.isEmpty(history.state.current)) {
    _.assign(history.state, { current: from.fullPath });
  }
  next();
});

问题解决了,感谢大佬🙏 @dongchaoge

请问下,这段代码加再哪个文件里面

gaofeng222 avatar Jul 20 '22 03:07 gaofeng222

多个版本 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)
  }
}

感谢大佬

gaofeng222 avatar Jul 20 '22 05:07 gaofeng222

我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4

子应用配置了 base,使用路由匹配模式激活子应用

在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载

这个似乎是vue-router4的问题,有找到什么解决方案么?

devinRex avatar Aug 20 '22 03:08 devinRex

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,若有任何问题,可评论回复。

github-actions[bot] avatar Sep 20 '22 00:09 github-actions[bot]

我的情况正好相反,主应用使用 vue2 + router3,子应用使用 vue3 + vite + router4 子应用配置了 base,使用路由匹配模式激活子应用 在主应用切换一次路由后,再在子应用里切换路由,push 没有报错,但是会导致子应用重新挂载

这个似乎是vue-router4的问题,有找到什么解决方案么?

解决了,用上面大佬说的方案改下低版本的 pushState 就行

youthug avatar Oct 25 '22 05:10 youthug

我参考上面的方法并没有解决我的问题,在 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);
      };
  }

linhongdong avatar Jun 01 '23 06:06 linhongdong

我的情况正好相反,主应用使用 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 字符的处理存在差异导致的

youthug avatar Jun 08 '23 11:06 youthug

我的情况正好相反,主应用使用 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。子应用内路由切换会重载子应用,并且浏览器回退会无法匹配到路由

hello-zsh avatar Dec 26 '23 05:12 hello-zsh