记录一次微前端场景下的热更新问题

492 阅读3分钟

最近在工作过程中发现,子应用的代码更新后,主应用直接进行了reload,并没有热更新,主应用是一个迭代7年之久的巨石应用,每次子应用的一点小改动都会导致主应用reload,对于开发效率是个不小的影响。

这篇文章用来记录一下排查解决的过程。

微前端框架用的qiankun,主子应用均使用webpack5进行构建

webpack的热更新过程

回顾一下 webpack dev 环境的整个热更新过程:

  • 启动 dev server,并创建 websocket 链接
  • 开启 webpack 编译,并监听文件变化,实时输出编译结果,同时修改 entry 配置,加入 dev-server.jsclient/index.js 文件,这两个文件用于websocket通信以及更新检查
  • 在编译完成的钩子中,通过 websocket 通知浏览器编译完成
  • 浏览器收到编译完成的消息后,向dev server请求 xxx.hash.hot-update.json / xxx.hash.hot-update.js 文件
  • 浏览器端替换旧的模块,并执行最新的代码,视图更新

以下 ①-④ 的小标记,是文件发生变化的一个流程

image.png

详细的热更新过程,可以查看原文 🎨 一文了解 Webpack 热更新 (HMR) 原理

问题分析

开发环境下,通过qiankun方式启动启动微应用和独立启动微应用,主要区别在于请求的方式运行的环境。在请求方式上,qiankun是通过浏览器的fetch API拉取前端资源,而微应用独立启动时是由浏览器直接发起的资源请求,二者始终请求的都是同一份文件,不应该有区别,所以问题应该出在运行环境上。

qiankun的运行环境browser-vm沙箱,这个沙箱本质上只是拦截代理了部分window/document上的API,并不会影响到热更新需要用到websocket能力。所以理论上,以qiankun方式运行的子应用 完全可以支持webpack热更新的能力。

运行环境虽然理论上可以支持,但运行的上下文信息不一定相同,我们 debugger 确认一下:

解决过程

在chrome控制台source面板中找到 dev-server.js 文件,在如图所在位置打个断点

image.png 修改代码,触发热更新流程,进入hot.check方法

image.png 这里setStatus方法主要用来设置当前更新状态以及触发相关回调,这里重点关注__webpack_require__.hmrM这个方法

image.png 可以看到这里通过fetch拉取热更新的资源,运行一步看一下

image.png 可以看到触发了404,直接返回了,继续调试

image.png 由于没有拉取到带更新的资源,所以这里直接触发的reload行为,回到之前fetch请求的地方

截屏2024-11-26 20.21.02.png__webpack_require__.p + __webpack_require__.hmrF()打印出来,可以看到,默认的资源地址是当前域名下的main.0bffdffeee4014ea0d9d.hot-update.json,而微前端环境下,主子应用运行在不同的 域名:端口 下,请求主应用 域名:端口 自然获取不到子应用的热更新信息。

这里拼接资源地址是用到了__webpack_require__.p这个属性,这个属性默认是/,可以由webpack配置中的output.publicPath指定,修改子应用开发环境的配置

image.png 重新触发热更新流程,回到fetch请求的地方 image.png 可以看到,此时请求的热更新资源地址已经正确,继续调试

image.png 可以看到成功拿到了热更新所需要的资源,后续将正常完成热更新的流程。

总结

webpackoutput.publicPath主要用来指定静态资源的加载地址,同时也会影响热更新时热更新文件的加载地址。在微前端场景下,子应用开发环境可以通过将publicPath配置为子应用的绝对地址来避免由此引发的热更新问题。