解决 single-spa 的 history BUG 及给npm包打补丁的方法

date
Sep 12, 2023
slug
patch-package
status
Published
summary
tags
Bug
前端
type
Post

single-spa 的 history BUG

一个微前端项目里,使用的是qiankun(基于single-spa封装的)。其中一个子应用用的是DvaJS,里面的路由使用的是Dva里面提供的路由。显示出来的问题是,路由页面会重复渲染两次。原因是因为 qiankun 所使用的 single-spa 改写了 window.history.pushState 方法,在每次 pushState 的时候会主动 dispatch 一个 popState 事件,被 history checkDomListeners 捕获,这样通过 history.listen 注册的监听函数都会被执行两次。而为什么其它子应用没这样的问题呢?因为那些子应用都只有一个 history(它们所引用的 react-router 是主应用提供出来的,只有一个实例存在),所以监听函数只会执行一次。而使用了dva的子应用,由于里面会有两个history,导致监听函数注册了两次,解决方案就是在子应用的 history 包里增加一个判断逻辑,当 popState 时候如果是 single-spa 触发的那就不要执行。
只需要更改这个文件 createBrowserHistory.js 即可:
解决的关键点就是如何修改子应用里所引用的 history,正常的话是只能使用包发布者提供的版本。

初步解决方案

发布私包到公司内部npm仓库中,通过一个不存在的version来锁死版本,强制安装自己修改过的history包。
history包目前发布的版本是 4.10.1 ,手动下载其源码后升为 4.10.3 ,然后发布到内部npm仓库中。简单的原理是,当用户从私有仓库拉取的包不存在时,Packages 会从设置代理仓库中拉取。
手动将有问题的子应用项目里的 history 锁死版本:
打包发布后,问题似乎解决了,子应用里的路由刷新时不会更新2次了。
但是,不出意外的话,意外发生了:后来本地开发的时候,某个子应用里的path更新后,路由并没有加载。原因就是公司里其它项目需要拉取 history 包的时候,也拉取到了修改后的包,出现了新问题。
经过一番查找资料,寻找了有没更优雅的解决方案,看到其中一篇文章:手把手教你使用patch-package给npm包打补丁 - 掘金 (juejin.cn)。里面除了patch-package,还列举了其它的一些手段,我一开始使用的这种粗暴的方法其实也在其中。

最终解决方案

  1. 安装 patch-package
  1. 修改npm包
打开目标项目代码 node_modules 文件夹里面的 history 文件夹,确认是4.10.1版本。
建一个临时目录,把 history 包4.10.1的源码下载下来,修改其中的代码。运行打包命令,把打包后的产物直接替换上述目录里。
  1. 生成补丁
此时在根目录下会得到如下文件:
notion image
至此补丁文件已经生成完毕,我们需要将它提交到git中,直接执行常规git操作即可。
  1. 完善 npm 脚本
基于上述操作我们在npm install后执行patch-package命令即可,这个流程可借助 npm script 实现,在 package.json 的 script 中添加如下字段及内容:
执行一次完成的「依赖安装 -> 构建发布」,就大功告成了。

CI(Jenkins)的坑

在使用 Jenkins 构建项目发布的时候,发现其实并没有执行 postinstall 命令,可以使用 hook 来解决。我采用的方法是在构建的时候主动运行一下命令:

© Stanley Wind 2023 - 2024