WebPack热更新原理
# 什么是HMR
HMR
即Hot Module Replacement
是指当你对代码修改并保存后,webpack
将会对代码进行重新打包,并将改动的模块发送到浏览器端,浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面。
在热更新的过程中有几个重要角色需要提前了解一下,那就是WDS (Webpack-dev-server)
和HMR(HotModuleReplacementPlugin)
# HMR的运转流程
看一下我们run dev
后webpack到底做了哪些事情
看流程图已经能看的非常清楚了,本质上其实就是wds在本地通过express去启动了一个web服务器,同时创建了一个webScoket服务
。而HMR则会在bundle里面注入一些热更新相关的代码,其中就包括了客户端连接scoket的代码。这样服务端和客户端就有了一个长连接
webpack会监听文件的变化,每当重新触发了webpack编译后,就会生成一个Hash值,一个json文件,一个js文件(可能没有)
当编译完成后,webpack会通过socket将本次编译后的Hash值发送给客户端,客户端接收到这个值之后会和上一次的Hash做比对
如果一致则走缓存,不一致则发送xxx/hash.hot-update.json
的请求,获取热更新的模块以及下次热更新的Hash
随后又会通过jsonP
的方式去将本次更新的js文件加载回来。
至于这里为什么要用jsonP
呢? 因为jsonP是请求回来的文件是立即执行的,而如果直接将更新后的代码放在scoket中发送的话
还得再做一步代码的处理,所以此处使用jsonP是最方便的。
# hotApply 热更新模块替换
在拿到最新的模块文件之后,要做的就是去替换掉原来老的模块了。
通过hotUpdate
可以找到旧模块
var queue = outdatedModules.slice();
while (queue.length > 0) {
moduleId = queue.pop();
// 从缓存中删除过期的模块
module = installedModules[moduleId];
// 删除过期的依赖
delete outdatedDependencies[moduleId];
// 存储了被删掉的模块id,便于更新代码
outdatedSelfAcceptedModules.push({
module: moduleId
});
}
2
3
4
5
6
7
8
9
10
11
12
13
将新的模块添加到 modules 中
appliedUpdate[moduleId] = hotUpdate[moduleId];
for (moduleId in appliedUpdate) {
if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
modules[moduleId] = appliedUpdate[moduleId];
}
}
2
3
4
5
6
通过__webpack_require__执行相关模块的代码
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
var item = outdatedSelfAcceptedModules[i];
moduleId = item.module;
try {
// 执行最新的代码
__webpack_require__(moduleId);
} catch (err) {
// ...容错处理
}
}
2
3
4
5
6
7
8
9
10
# 总结
总结一下就是webpack通过WDS插件启动本地服务器托管静态资源,同时创建scoket服务(HMR 会在bundle里注入runtime,来连接此scoket服务)。然后WDM插件开始监听webpack文件的编译 以便随时通知本地服务器代码变更了。而HMR插件则是在最终打包成的bundle里注入了一系列处理热更新的代码。
webpack每次编译后都会产生一个hash串,并将这个串发送给客户端,客户端就是依据这个串来分别代码是否变更。 如果代码变更了 客户端就会发起一个http请求,去请求一个json文件。json文件里包含了本次更新的模块以及下一次更新所需要的hash值 在做完这些事情之后,HMR会通过jsonP的方式去请求最新的模块资源,然后通过内部的一些手段去进行新老模块的替换