看完这篇,面试再也不怕被问 Webpack 热更新,java 原理及插件
This module is only concerned with the mechanisms to connect a browser client to a Webpack server & receive updates.
Webpack-hot-middleware 插件的作用就是提供浏览器和 Webpack 服务器之间的通信机制、且在浏览器端接收 Webpack 服务器端的更新变化。
为了更好的理解这一段话,打开浏览器开发者调试工具,可以看到在 Webpack 打包好的 Js 中主要包含了以下几部分。下面截取关键部分进行说明:
Webpack-hot-middleware/client.js
源码中有这么一段配置,看到这里瞬间想到了在开发时浏览器的 Network 中总是有一个 __Webpack_hmr 的请求,点开查看会看到 EventStream 事件流(服务器端事件流,服务器向浏览器推送消息,除了 websocket 全双工通道双向通信方式还有一种 Server-Sent Events 单向通道的通信方法,只能服务器端向浏览器端通过流信息的方式推送消息;页面可以通过 EventSource 实例接收服务器发送事件通知并触发 onmessage 事件),并且以 2s 的频率不停的更新消息内容,每行消息内容都有 ?? 的图标,没错这就是一个心跳请求。
var options = {
path: '/__Webpack_hmr',
timeout: 20 * 1000,
overlay: true,
reload: false,
log: true,
warn: true,
name: '',
autoConnect: true,
overlayStyles: {},
overlayWarnings: false,
ansiColors: {},
};
复制代码
继续向下查看 Client.js 代码,发现这完全就是一个只要浏览器支持就可以自发建立通信通道的客户端。
if (typeof window === 'undefined') {
// do nothing
} else if (typeof window.EventSource === 'undefined') {
// warning
} else {
if (options.autoConnect) {
// 建立通信连接
connect();
}
}
复制代码
在建立通信的过程中,浏览器端会初始化一个 EventSource 实例并通过 onmessage 事件监听消息。浏览器端在收到服务器发来的数据时,就会触发 onmessage 事件,可以通过定义 onmessage 的回调函数处理接收到的消息。
// 浏览器端建立连接
function EventSourceWrapper() {
var source;
var listeners = [];
// 初始化 EventSource 实例
source = new window.EventSource(options.path);
// 定义 onmessage 事件监听服务器端消息返回
source.onmessage = handleMessage;
function handleMessage(event) {
for (var i = 0; i < listeners.length; i++) {
listenersi;
}
}
return {
addMessageListener: function(fn) {
listeners.push(fn);
}
};
}
// 浏览器端建立通信通道,监听处理服务器端推送的消息
function connect() {
EventSourceWrapper().addMessageListener(handleMessage);
function handleMessage(event) {
try {
processMessage(JSON.parse(event.data));
} catch (ex) {
// handler exception
}
}
}
复制代码
Client.js 监听的消息有:
building/built:构建中,不会触发热更新; ?
sync:开始更新的流程。
在 processUpdate 方法中,处理一切异常/错误的方法都是直接更新整个页面即调用 window.location.reload(),首先调用 module.hot.check 方法检测是否有更新,然后进入 HotModuleReplacement.runtime 的 Check 阶段。
function processMessage(obj) {
switch (obj.action) {
case 'building':
// tell you rebuilding
break;
case 'built':
// tell you rebuilt in n ms
// fall through
case 'sync':
// 省略...
var applyUpdate = true;
if (applyUpdate) {
processUpdate(obj.hash, obj.modules, options);
}
break;
default:
// do something
}
}
复制代码
热更新过程
改动页面代码保存之后,Webpack 会重新编译文件并发消息通知浏览器,浏览器在 Check 之后触发 WebpackHotUpdateCallback,具体 HotModuleReplacement.runtime.js 会做以下几个操作:
进入 HotCheck,调用 hotDownloadManifest 发送 /hash.hot-update.json 请求;
通过 Json 请求结果获取热更新文件,以及下次热更新的 Hash 标识,并进入热更新准备阶段;
hotAvailableFilesMap = update.c;// 需要更新的文件
hotUpdateNewHash = update.h;// 下次热更新 hash 值
hotSetStatus("prepare");// 进入热更新准备状态
复制代码
HotCheck 确认需要热更新之后进入 hotAddUpdateChunk 方法,该方法先检查 Hash 标识的模块是否已更新,如果没更新,则通过在 DOM 中添加 Script 标签的方式,动态请求 js: /fileChunk.hash.hot-update.js,获取最新打包的 js 内容;
最新打包的 js 内容如何更新的呢?HotModuleReplacement.runtime.js 在 window 对象上定义了 WebpackHotUpdate 方法;在这里定义了如何解析前面 fileChunk.hash.hot-update.js 请求返回的 js 内容 webpackHotUpdate(main1, { moreModules }),直接遍历 moreModules,并且执行 hotUpdate 方法更新;
![image-20190925145730597](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9
1c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOS85LzI1LzE2ZDY4YzQ0NTI4N2JiMTk_aW1hZ2VWaWV3Mi8wL3cvMTI4MC9oLzk2MC9mb3JtYXQvd2VicC9pZ25vcmUtZXJyb3IvMQ?x-oss-process=image/format,png)
评论