這篇文章就先把結論放在最前面啦,應該有利於閱讀體驗。前端
上回書說到git的用法,這篇文章就說下我學習webpack熱更新原理的記錄。vue
仍是由於上次面試的事,面試官問我是否知道webpack熱更新的原理,我回答的是我使用vue-cli3構建項目,初始化的時候,webpack集成了一個能夠監聽文件變化,而且通知頁面刷新的組件,他問能詳細點嗎,我...node
回家後,打開電腦,運行項目,打開瀏覽器控制檯,選擇 network ,先清除以前的加載記錄。這時候去修改個人項目文件,保存,回來看下控制檯,再修改,再回來看下控制檯,這個時候讓我發現了這個:webpack
一個hot-update.json文件和一個hot-update.js文件。有些年頭的前端開發者會發現,hot-update.js返回的內容,怎麼很像jsonp返回的內容。我忽然以爲,我是否是要入門了,懷着激動的心情,我四處探索,如今按正確的時間線梳理一下。git
1.保持瀏覽器控制檯,刷新網頁,以下圖,關鍵點在於 app.js,hot-update.js,websocket。github
2.打開app.js,很快能找到以下代碼web
3.修改文件,控制檯會變成以下面試
4.同時頁面中也會插入一個scriptvue-cli
從以上截圖,我先作個推論:wepack在啓動的時候開啓一個node服務,這個服務經過websocket與瀏覽器保持持續的通訊,在檢測到文件發生變化時,服務端向瀏覽器發送一個json和一個js文件。同時每次服務端發送的消息的 hash 將做爲下次 hot-update.json 和 hot-update.js 文件的 hash值。json
綜合上面的分析,我以爲我已經徹底掌握熱更新流程,本文結束,嘻嘻睡了...
上面的推論是錯的,也不是徹底錯,只是細節錯了。
一開始個人目標是直接查看webpack的源碼,裏面有一個hot的文件夾,在大概讀了其中代碼,只在裏面找到了一些log輸出和方法,沒有找到在哪裏調用的。感謝 HMR的原理 這篇文章讓我找明瞭方向。代碼是在webpack-dev-server的源碼下面= =
下面就開始正文了:
當咱們構建項目的時候,webpack-dev-server會爲咱們本身的js文件包上一層代碼,加上兩個依賴:
// 這個模塊是新加的,咱們的入口就是 index,而這裏加了一個模塊,引用了 index,而且額外加了兩行 require
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__("./node_modules/webpack-dev-server/client/index.js?http://localhost:8080");
__webpack_require__("./node_modules/webpack/hot/dev-server.js");
module.exports = __webpack_require__("./src/index.js");
/***/ })
/******/ })
複製代碼
其中client/index.js主要負責創建socket通訊,爲咱們提供一些監聽方法等等,dev-server提供熱更新的方法。
client/index.js 中有以下代碼:
const onSocketMsg = {
hash: function msgHash(hash) { // 在 `hash` 事件觸發的時候,把 `hash` 記下來
currentHash = hash;
},
ok: function msgOk() { // `ok` 事件觸發的時候,表示server已經便已完成最新代碼,
// ...
reloadApp();
}
};
// ...
function reloadApp() {
if (hot) {
log.info('[WDS] App hot update...');
// eslint-disable-next-line global-require
const hotEmitter = require('webpack/hot/emitter');
hotEmitter.emit('webpackHotUpdate', currentHash) // 觸發這個事件
}
// ...
}
複製代碼
hot/dev-server.js中:
hotEmitter.on("webpackHotUpdate", function(currentHash) {
lastHash = currentHash;
if (!upToDate() && module.hot.status() === "idle") {
log("info", "[HMR] Checking for updates on the server...");
check(); // 觸發check方法
}
});
var check = function check() {
module.hot
.check(true) // 這裏的check方法最終進入到webpack\lib\HotModuleReplacement.runtime.js文件中
...
}
複製代碼
HotModuleReplacement.js中有:
function hotCheck(apply) {
...
return hotDownloadManifest(hotRequestTimeout).then(function(update) {
...
{
// 取到了 manifest後,就能夠經過jsonp 加載最新的模塊的JS代碼了
hotEnsureUpdateChunk(chunkId);
}
...
});
}
複製代碼
最終經過jsonp的方式加載,加載js的代碼以下:
function hotDownloadUpdateChunk(chunkId) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.charset = "utf-8";
script.src = $require$.p + $hotChunkFilename$;
if ($crossOriginLoading$) script.crossOrigin = $crossOriginLoading$;
head.appendChild(script);
}
複製代碼
到此爲止,咱們就已經獲得了新模塊的JS代碼了,下面就是調用對應的accpet回調。
本文結束,這些內容已經有不少人寫過啦,本文只是用來記錄本身的學習歷程和加深本身的印象,若有錯誤,請指正!