HMR 最初由 Webpack 设计实现,至今已几乎成为现代工程化工具必备特性之一。
1.1 HMR 之前
在 HMR 之前,应用的加载、更新是一种页面级别的原子操作,即使只是单个代码文件发生变更都需要刷新整个页面才能最新代码映射到浏览器上,这会丢失之前在页面执行过的所有交互与状态,例如:
-
对于复杂表单场景,这意味着你可能需要重新填充非常多字段信息
-
弹框消失,你必须重新执行交互动作才会重新弹出
再小的改动,例如更新字体大小,改变备注信息都会需要整个页面重新加载执行,影响开发体验。引入 HMR 后,虽然无法覆盖所有场景,但大多数小改动都可以实时热更新到页面上,从而确保连续、顺畅的开发调试体验,对开发效率有较大增益效果。
1.2 使用 HMR
Webpack 生态下,只需要经过简单的配置即可启动 HMR 功能,大致上分两步:
- 配置
devServer.hot
属性为 true,如:
// webpack.config.js
module.exports = {
// …
devServer: {
// 必须设置 devServer.hot = true,启动 HMR 功能
hot: true
}
};
- 之后,还需要调用
module.hot.accept
接口,声明如何将模块安全地替换为最新代码,如:
import component from “./component”;
let demoComponent = component();
document.body.appendChild(demoComponent);
// HMR interface
if (module.hot) {
// Capture hot update
module.hot.accept(“./component”, () => {
const nextComponent = component();
// Replace old content with the hot loaded one
document.body.replaceChild(nextComponent, demoComponent);
demoComponent = nextComponent;
});
}
模块代码的替换逻辑可能非常复杂,幸运的是我们通常不太需要对此过多关注,因为业界许多 Webpack Loader 已经提供了针对不同资源的 HMR 功能,例如:
-
style-loader
内置 Css 模块热更 -
vue-loader
内置 Vue 模块热更 -
react-hot-reload
内置 React 模块热更接口
因此,站在使用的角度,只需要针对不同资源配置对应支持 HMR 的 Loader 即可,很容易上手。
二、实现原理
======
Webpack HMR 特性的原理并不复杂,核心流程:
-
使用
webpack-dev-server
(后面简称 WDS)托管静态资源,同时以 Runtime 方式注入 HMR 客户端代码 -
浏览器加载页面后,与 WDS 建立 WebSocket 连接
-
Webpack 监听到文件变化后,增量构建发生变更的模块,并通过 WebSocket 发送
hash
事件 -
浏览器接收到
hash
事件后,请求manifest
资源文件,确认增量变更范围 -
浏览器加载发生变更的增量模块
-
Webpack 运行时触发变更模块的
module.hot.accept
回调,执行代码变更逻辑 -
done
接下来我会展开 HMR 的核心源码,详细讲解 Webpack 5 中 Hot Module Replacement 原理的关键部分,内容略微晦涩,不感兴趣的同学可以直接跳到下一章。
2.1 注入 HMR 客户端运行时
执行 npx webpack serve
命令后,WDS 调用 HotModuleReplacementPlugin
插件向应用的主 Chunk 注入一系列 HMR Runtime,包括:
-
用于建立 WebSocket 连接,处理
hash
等消息的运行时代码 -
用于加载热更新资源的
RuntimeGlobals.hmrDownloadManifest
与RuntimeGlobals.hmrDownloadUpdateHandlers
接口 -
用于处理模块更新策略的
module.hot.accept
接口 -
等等
关于 Webpack Runti me,可参考 Webpack 原理系列六:彻底理解 Webpack 运行时 。
经过 HotModuleReplacementPlugin
处理后,构建产物中即包含了所有运行 HMR 所需的客户端运行时与接口。这些 HMR 运行时会在浏览器执行一套基于 WebSocket 消息的时序框架,如图:
2.2 增量构建
除注入客户端代码外,HotModuleReplacementPlugin
插件还会借助 Webpack 的 watch
能力,在代码文件发生变化后执行增量构建,生成:
-
manifest
文件:JSON 格式文件,包含所有发生变更的模块列表,命名为[hash].hot-update.json
-
模块变更文件:js 格式,包含编译后的模块代码,命名为
[hash].hot-update.js
增量构建完毕后,Webpack 将触发 compilation.hooks.done
钩子,并传递本次构建的统计信息对象 stats
。WDS 则监听 done
钩子,在回调中通过 WebSocket 发送模块更新消息:
{“type”:“hash”,“data”:“${stats.hash}”}
实际效果:
2.3 加载更新
客户端接受到 hash
消息后,首先发出 manifest
请求获取本轮热更新涉及的 chunk,如:
注意,在 Webpack 4 及之前,热更新文件以模块为单位,即所有发生变化的模块都会生成对应的热 更新文件; Webpack 5 之后热更新文件以 chunk 为单位,如上例中,
main
chunk 下任意文件的变化都只会生成main.[hash].hot-update.js
更新文件。
manifest
请求完成后,客户端 HMR 运行时开始下载发生变化的 chunk 文件,将最新模块代码加载到本地。
2.4module.hot.accept
回调
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
结尾
学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。
[外链图片转存中…(img-4uE2rOww-1712781163967)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-iRTob2Hb-1712781163967)]