最近在工作过程中发现,子应用的代码更新后,主应用直接进行了reload,并没有热更新,主应用是一个迭代7年之久的巨石应用,每次子应用的一点小改动都会导致主应用reload,对于开发效率是个不小的影响。
这篇文章用来记录一下排查解决的过程。
微前端框架用的
qiankun,主子应用均使用webpack5进行构建
webpack的热更新过程
回顾一下 webpack dev 环境的整个热更新过程:
- 启动 dev server,并创建 websocket 链接
- 开启 webpack 编译,并监听文件变化,实时输出编译结果,同时修改 entry 配置,加入 dev-server.js 和 client/index.js 文件,这两个文件用于websocket通信以及更新检查
- 在编译完成的钩子中,通过 websocket 通知浏览器编译完成
- 浏览器收到编译完成的消息后,向dev server请求 xxx.hash.hot-update.json / xxx.hash.hot-update.js 文件
- 浏览器端替换旧的模块,并执行最新的代码,视图更新
以下 ①-④ 的小标记,是文件发生变化的一个流程
详细的热更新过程,可以查看原文 🎨 一文了解 Webpack 热更新 (HMR) 原理
问题分析
开发环境下,通过qiankun方式启动启动微应用和独立启动微应用,主要区别在于请求的方式和运行的环境。在请求方式上,qiankun是通过浏览器的fetch API拉取前端资源,而微应用独立启动时是由浏览器直接发起的资源请求,二者始终请求的都是同一份文件,不应该有区别,所以问题应该出在运行环境上。
qiankun的运行环境是browser-vm沙箱,这个沙箱本质上只是拦截代理了部分window/document上的API,并不会影响到热更新需要用到websocket能力。所以理论上,以qiankun方式运行的子应用 完全可以支持webpack热更新的能力。
运行环境虽然理论上可以支持,但运行的上下文信息不一定相同,我们 debugger 确认一下:
解决过程
在chrome控制台source面板中找到 dev-server.js 文件,在如图所在位置打个断点
修改代码,触发热更新流程,进入
hot.check方法
这里
setStatus方法主要用来设置当前更新状态以及触发相关回调,这里重点关注__webpack_require__.hmrM这个方法
可以看到这里通过
fetch拉取热更新的资源,运行一步看一下
可以看到触发了404,直接返回了,继续调试
由于没有拉取到带更新的资源,所以这里直接触发的
reload行为,回到之前fetch请求的地方
将
__webpack_require__.p + __webpack_require__.hmrF()打印出来,可以看到,默认的资源地址是当前域名下的main.0bffdffeee4014ea0d9d.hot-update.json,而微前端环境下,主子应用运行在不同的 域名:端口 下,请求主应用 域名:端口 自然获取不到子应用的热更新信息。
这里拼接资源地址是用到了__webpack_require__.p这个属性,这个属性默认是/,可以由webpack配置中的output.publicPath指定,修改子应用开发环境的配置
重新触发热更新流程,回到
fetch请求的地方
可以看到,此时请求的热更新资源地址已经正确,继续调试
可以看到成功拿到了热更新所需要的资源,后续将正常完成热更新的流程。
总结
webpack的output.publicPath主要用来指定静态资源的加载地址,同时也会影响热更新时热更新文件的加载地址。在微前端场景下,子应用开发环境可以通过将publicPath配置为子应用的绝对地址来避免由此引发的热更新问题。