遇到的问题:当子应用切换过一次路由后,主应用切换路由会报错
项目信息
主应用:
使用Vue CLI 4 搭建(ts、3.x、router、history)
"qiankun": "^2.4.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0"
关键代码如下
main.ts
App.vue
router/index.ts
子应用:
使用Vue CLI 4 搭建(2.x、router、hash)
"vue": "^2.6.11",
"vue-router": "^3.2.0"
关键代码如下
main.js
问题描述
当子应用切换过一次路由后,主应用路由报错
试过子应用使用vue3 & hash路由,能正常使用(但其实也存在问题,主应用的history.state还是被子应用修改了,只是数据结构和主应用一致所以未报错。子应用使用vue3 & history,添加base可以完美使用)。总的来说问题是主应用和子应用使用不同版本的vue(vue-router)造成的。
查看官方文档、仓库issues和搜索谷歌均未找到合适的解决方案。
操作步骤
- 点击子应用对应的路由(app-sub-vue2)
- 点击子应用的其他路由(About)
- 点击主应用其他路由(Home)
现在的解决方案
- 在子应用里修改:在子应用切换路由后手动调用一次window.history.pushState(null,'',''),调用后主应用路由可正常使用
- 在主应用修改:通过router.beforeEach判断history.state数据结构是否异常,若异常则重新设置
分析
通过debugger找到报错位置为主应用的vue-router
在切换主应用路由时,会调用push(vue-router/history/html5.ts)方法
在265行会先调用一次changeLocation用来记录当前路由信息(中间状态)
也就是在这一步执行的时候,在方法
changeLocation(history/html5.ts)执行到history.replaceState、locatin.replace(214、223)方法的时候由于url(http://localhost:6700undefined)无效而报错,从207行看出,undefined其实就是to这个入参,是从外部传入的。
正常情况下,这一步操作后这里传入的to应该为/app-sub-vue2#/about,表示当前的路由信息,但是这里传入的to成了undefined。
又回到push,在调用changeLocation的时候传入的to其实是由两个变量合并而来的(247行),一个是内部变量historyState,另一个是全局对象history的state属性,然后我分别看了下这两个变量
首先看history的state属性,history是全局对象,那么在子应用切换路由的时候,很可能会改变到它的属性,经过验证,在子应用切换路由的时候也确实修改到了history.state,但是子应用应该只能修改代理的fakeWindow上的数据才对(后面发现其实不是),也就是主应用挂载到window.proxy下的数据(尝试了一下发现window.proxy.history === window.history),所以推断这里应该是主应用监听的popstate被子应用触发了
又看了下另外一个变量
historyState,主应用的popstate监听函数被子应用触发后就直接给它赋值了
接下来去看了下qiankun的代码,在js沙箱相关处理的地方发现,当子应用获取history时,其实返回的就是主应用的history对象,所以它们使用的是同一个history对象,那么自然当子应用修改history信息时,主应用也会被影响到。
疑惑
上面提到的手动调用一次window.history.pushState(null,'',''),其实也是因为传入的state为null,然后在vue-router里面有做判断。但是这个处理方法需要侵入子应用的业务代码来操作,感觉不太好。
所以,主应用、子应用在操作history时,会相互影响,但是vue-router的3.x和4.x版本存储history.state的时候数据结构又不相同,那么该如何来解决这个问题呢?