umijs / qiankun

📦 🚀 Blazing fast, simple and complete solution for micro frontends.
https://qiankun.umijs.org
MIT License
15.84k stars 2.02k forks source link

vue2作为主应用,使用vue3子应用时,在子应用中切换路由又回退时出现了url自动加上了undefined #2254

Open carolin-violet opened 2 years ago

carolin-violet commented 2 years ago

What happens?

A clear and concise description of what the bug is.

Mini Showcase Repository(REQUIRED)

Provide a mini GitHub repository which can reproduce the issue.

How To Reproduce

Steps to reproduce the behavior: 1. 2.

Expected behavior 1. 2.

Context

Nauxscript commented 2 years ago

我也遇到了类似的情况,在 father 中进入 child 的页面,如 /child/a,然后从 /child/a 通过 child 自己的 router 跳转,即 router.push({path: '/b'}) 跳转到 /child/b 页面,这时在 /child/b 使用 router.back() 方法,会跳转到空白页面,地址栏显示的地址为 /childundefined

child: vue 3.2 / vue-router 4.x father: vue 2.6.x / qiankun 2.4.8

zyf540860996 commented 2 years ago

我也遇到了类似的情况

CarsonXin commented 2 years ago

我也有类似的情况,哪怕用浏览器的后退按钮或history.go(-1)也会有这样的问题 父应用:vue@^2.6.11 + vue-router@^3.0.6 + qiankun@^2.4.2 子应用:vue@^3.2.13 + vue-router@^4.0.3

ripeBoy commented 2 years ago

+1 父应用:vue@2.6.14 + vue-router@3.5.3 + qiankun@2.7.0 子应用:vue@3.2.37 + vue-router@4.1.3

carolin-violet commented 2 years ago

一开始的注册方法

`import { registerMicroApps, start } from 'qiankun';

registerMicroApps([ { name: 'react app', // app name registered entry: '//localhost:7100', container: '#yourContainer', activeRule: '/yourActiveRule', }, { name: 'vue app', entry: { scripts: ['//localhost:7100/main.js'] }, container: '#yourContainer2', activeRule: '/yourActiveRule2', }, ]);

start();`

换了第二种注册方法就不会有undefined问题了,但不知道为什么

`import { loadMicroApp } from 'qiankun';

loadMicroApp({ name: 'app', entry: '//localhost:7100', container: '#yourContainer', });`

ripeBoy commented 2 years ago

一开始的注册方法

`import { registerMicroApps, start } from 'qiankun';

registerMicroApps([ { name: 'react app', // app name registered entry: '//localhost:7100', container: '#yourContainer', activeRule: '/yourActiveRule', }, { name: 'vue app', entry: { scripts: ['//localhost:7100/main.js'] }, container: '#yourContainer2', activeRule: '/yourActiveRule2', }, ]);

start();`

换了第二种注册方法就不会有undefined问题了,但不知道为什么

`import { loadMicroApp } from 'qiankun';

loadMicroApp({ name: 'app', entry: '//localhost:7100', container: '#yourContainer', });`

https://github.com/vuejs/router/pull/1219

maikangzhi commented 1 year ago

解决了吗

Ashuangshuang commented 1 year ago

@Nauxscript @zyf540860996 @CarsonXin @ripeBoy 各位,我现在也遇到这个问题了,请问你们解决了吗?

maikangzhi commented 1 year ago

@Ashuangshuang

在子应用push 的时候,使用下面其中一种都可以 1,history.pushState(null, '', '#/router-path') 2,主路由 push(‘#/router-path’) 目前我没找到统一的方法,上面两种都可以用来应急,第二种方法是,主应用通过props传它的路由过来。我的做法是在子应用封装了一个push 方法, export function push(param) { if (!qiankunWindow.POWERED_BY_QIANKUN) { router.push(param) } else { if (typeof param === 'string') { param = '#' + param } else { param.path = '#' + param.path } if (mainRouter) { mainRouter.push(param) } else { history.pushState(null, '', param?.path || param) } } }

lvchengli commented 1 year ago

@Nauxscript @zyf540860996 @CarsonXin @ripeBoy 各位,我现在也遇到这个问题了,请问你们解决了吗?

@CarsonXin @Nauxscript @zyf540860996 @carolin-violet @ripeBoy @maikangzhi 可以看下我的解决方案。https://github.com/lvchengli/qiankun-demo/tree/feature/fix

// vue2 主应用 + vue3 子应用。在主应用中修改 history,去除子应用的路由前缀
function getPathStr(str) {
  if (str.startsWith('/vue3')) {
    return str.replace('/vue3', '')
  } 
  return str
}

router.afterEach((to, from, next) => {
  console.log(to.fullPath, 'router.afterEach')
  const state = {
    ...history.state,
    current: getPathStr(to.fullPath)
  }
  history.replaceState(state, '', window.location.href)
})

// vue3 主应用 + vue2 子应用。在子应用中修改 history,在router路径前增加子应用的路由前缀
const base = window.__POWERED_BY_QIANKUN__ ? '/vue' : ''

router.afterEach((to, from, next) => {
  const state = {
    ...history.state,
    current: base + to.fullPath
  }
  history.replaceState(state, '', window.location.href)
})
lsd1 commented 1 year ago

@lvchengli @carolin-violet @Ashuangshuang @ripeBoy @maikangzhi 可以尝试通过这种方式解决: 子应用使用push方法要注意,不能使用push({ name: 'xxx' })以及push({ path: '带父级的完整路径' })方式去跳转 应该使用push({ path: '子页面路由' }),并且去掉斜杠前面的 '/' 这样就能避免history.back(),或者浏览器后退按钮回退页面是导致路径被追加undefined的问题

eg: 这是一个子应用的路由,里面有多级嵌套 { path: '/', name: 'home', redirect: '/security-manage/dashboard', children: [{ path: '/security-manage', name: 'SecurityManage', component: () => import('@/views/securityManage/Index.vue'), redirect: '/security-manage/dashboard', meta: { title: '安全管理', }, children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/securityManage/dashboard/Index.vue') } ] }] }

如果我要跳转到dashboard页面有以下方式都可以完成跳转

  1. router.push({ name: 'Dashboard' })
  2. router.push({ path: '/security-manage/dashboard' })
  3. router.push({ path: 'security-manage/dashboard' }) (比方法二少了一个‘/’)
  4. router.push({ path: 'dashboard' })(比方法三少了一个‘父级路径’) 使用方法1、2时,如果触发返回,会导致路由后面被追加了个undefined 方法3、4则不会
ygcedu commented 1 year ago

我在vue3作为主应用,react作为子应用时也出现了这个问题,最后发现是vue-router(v4.1.6)的push函数导致的错误,贴一下代码: image 出现错误的原因就在于子应用的react-router在路由跳转时并不会给currentState.current赋值,导致主应用vue-router这里会是undefined,进而导致如下错误: image 因此修改也比较简单,直接修改vue-router的源码,如下: image 详情见我提的一个pr:https://github.com/vuejs/router/pull/1811

lixioo commented 1 year ago

楼上找到的这个是根本原因,就是router大版本对currentState的修改方式不同导致的,不好改源码的话,可以自己加一个路由钩子,我这里引了lodash,自己写一个也可以 router.beforeEach((to, from, next) => { if (_.isEmpty(history.state.current)) { _.assign(history.state, { current: from.fullPath }); } next(); });

apm29 commented 1 year ago

我是主应用vue2.6 + vue-router3.x + webpack, 子应用vue3+vue-router4.x+vite+unplugin-vue-router,同域名下不同子路径,都是hash模式,注册方式是registerMicroApps+start,浏览器回退或者history.back就会出现undefined链接,用了这个方法下可以了

楼上找到的这个是根本原因,就是router大版本对currentState的修改方式不同导致的,不好改源码的话,可以自己加一个路由钩子,我这里引了lodash,自己写一个也可以 router.beforeEach((to, from, next) => { if (_.isEmpty(history.state.current)) { _.assign(history.state, { current: from.fullPath }); } next(); });

lihua24689 commented 10 months ago

楼上找到的这个是根本原因,就是router大版本对currentState的修改方式不同导致的,不好改源码的话,可以自己加一个路由钩子,我这里引了lodash,自己写一个也可以 router.beforeEach((to, from, next) => { if (_.isEmpty(history.state.current)) { _.assign(history.state, { current: from.fullPath }); } next(); });

这个方法下可以了,但是点击浏览器的返回的时候回不到上一个子应用的页面了,多次点击返回就报错了

Alone-2 commented 2 months ago

@Nauxscript @zyf540860996 @CarsonXin @ripeBoy 各位,我现在也遇到这个问题了,请问你们解决了吗?

@CarsonXin @Nauxscript @zyf540860996 @carolin-violet @ripeBoy @maikangzhi 可以看下我的解决方案。https://github.com/lvchengli/qiankun-demo/tree/feature/fix

// vue2 主应用 + vue3 子应用。在主应用中修改 history,去除子应用的路由前缀
function getPathStr(str) {
  if (str.startsWith('/vue3')) {
    return str.replace('/vue3', '')
  } 
  return str
}

router.afterEach((to, from, next) => {
  console.log(to.fullPath, 'router.afterEach')
  const state = {
    ...history.state,
    current: getPathStr(to.fullPath)
  }
  history.replaceState(state, '', window.location.href)
})

// vue3 主应用 + vue2 子应用。在子应用中修改 history,在router路径前增加子应用的路由前缀
const base = window.__POWERED_BY_QIANKUN__ ? '/vue' : ''

router.afterEach((to, from, next) => {
  const state = {
    ...history.state,
    current: base + to.fullPath
  }
  history.replaceState(state, '', window.location.href)
})

vue3 主应用加vue3子应用呢