如何解决微前端中的路由跳转坑
事件背景
商家工作台目前已经使用qiankun 改造成微前端体系,微前端的好处不用我赘述,大家懂的都懂,因此在有品二清项目开发时也以微前端的方式进行接入。在送测过程中,测试同学反馈在点击浏览器回退的时候会页面空白,我一脸蒙圈的试了好几次却没复现,最终在服务端同学跟测试同学的大力配合下,找到了稳定复现的场景。
问题定位
When you have eliminated the impossibles,whatever remains,however improbable,must be the truth. – 福尔摩斯
为什么会出现这个问题呢?
原因未知,需要继续拆解
以前接入过的子应用有遇到过这种问题呢?
就我所知没有
好的,为什么以前的子应用没有遇到过呢?
因为以前的子应用跟主应用都是vue2的router
那就是说可能是新旧Router导致的咯?
有可能哦~问问其他子应用呢
.....问过了,人家是好的
????.....再报再探!
以上是遇到问题时混乱的大脑的疑点闪现过程。从中我们不难找出导致问题的大致范围:主应用的Router版本与子应用的Router版本不一致,当然,中间还存在几个疑点,需要继续拨开迷雾。
实现原理有什么差异会导致这个问题。
其他Vue3项目为啥没遇到这个问题。
带着以上问题,我们开始进入漫长的断点debug 与翻源码过程。
源码分析
真実はいつもひとつ
1. 实现原理的差异首先,让我们来分析一波新旧版本的 Router 到底更改了什么。我们在新版本Router文档中找到这么一段说明。
稍后我们会在源码中找到对应的操作state的具体位置而旧版本的Router使用的pushState,则只在state中存储了key这唯一一个属性。(vue-router/src/util/push-state.js)
也就是说,history.state 在新旧版本的Router中的数据结构是不一致的。
我猜这应该是导致改问题的核心因素了。我们接着往下验证
2. undefined 的由来
主应用在点击左侧菜单的时候,会触发pushState,从而激活子应用,进入子应用对应的菜单中。
子应用在第一次进入的时候,会执行changeLocation的replace操作,此时页面进入子应用内。
子应用内部页面跳转时,所有的路由均由子应用Router进行接管。此时一切正常。
再次点击主应用左侧菜单时,执行以下代码将 history.state 置为空对象。同时,页面跳转到对应路由页面。
history.pushState({}, '', jumpUrl)
继续在子应用内部跳转路由时,如下图所示,当执行到 push(vue-router/history/html5.ts) 的时候,由于此时 history.state 已经变成了空对象,所以图中的 currentState.current 就变成了undefined。
在265行会先调用一次changeLocation用来记录当前路由信息(中间状态)
而进入到 changeLocation 方法内部,此时的 to 已经变成了undefined,也因此拼装的 url 中会出现undefined。
由于第六步中执行的是中间态的记录当前路由行为,后续还会继续跳转真实的目的地路由,所以中间态的undefined被保存在了浏览器的路由栈中。当触发回退的时候,就跳到了url中带有undefinedresole的空白页面。
现在,案情真相大白了。
解决方案
一劳永逸的方案则是:主应用与子应用均使用同一个版本的 VueRouter,当然,也要从实际出发,综合考虑各项改动的ROI,从而采用更合理的方案。
One More Thing!
为什么其他使用新Router的子应用没有遇到同样的问题?
我盲猜是因为 其他子应用要么只有一个主应用的菜单,要么子应用之间的菜单比较独立,所以没有遇到。实际上,这应该是个必现的场景。