爲了前端的深度-webpack熱更新

結論

這篇文章就先把結論放在最前面啦,應該有利於閱讀體驗。前端

  1. webpack構建的項目,分爲server端和client端(也就是瀏覽器),項目啓動時,雙方會保持一個socket鏈接,用來通話。
  2. 當webpack監聽到文件改動,server向client發送一個hash值,client保存下來,而後server再發送一個ok消息。client接收到表示知道能夠加載更新了,因而調用reloadApp方法。
  3. reloadApp 會調用 check方法,而後check調用hotDownloadManifest方法,這時候會下載咱們提到過的json文件。
  4. hotEnsureUpdateChunk完成後調用hotDownloadManifest方法,這個方法會經過jsonp的格式加載新的代碼。
  5. 加載完成以後,會有其餘的方法對其對比分析,用目前最快的方法去更新文件。

起源

上回書說到git的用法,這篇文章就說下我學習webpack熱更新原理的記錄。vue

仍是由於上次面試的事,面試官問我是否知道webpack熱更新的原理,我回答的是我使用vue-cli3構建項目,初始化的時候,webpack集成了一個能夠監聽文件變化,而且通知頁面刷新的組件,他問能詳細點嗎,我...node

回家後,打開電腦,運行項目,打開瀏覽器控制檯,選擇 network ,先清除以前的加載記錄。這時候去修改個人項目文件,保存,回來看下控制檯,再修改,再回來看下控制檯,這個時候讓我發現了這個:webpack

1

一個hot-update.json文件和一個hot-update.js文件。有些年頭的前端開發者會發現,hot-update.js返回的內容,怎麼很像jsonp返回的內容。我忽然以爲,我是否是要入門了,懷着激動的心情,我四處探索,如今按正確的時間線梳理一下。git

1.保持瀏覽器控制檯,刷新網頁,以下圖,關鍵點在於 app.js,hot-update.js,websocket。github

2

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回調。

本文結束,這些內容已經有不少人寫過啦,本文只是用來記錄本身的學習歷程和加深本身的印象,若有錯誤,請指正!

本文參考:

HMR的原理 (特別鳴謝,寫的真好)

Webpack的HMR原理分析

Webpack 熱更新實現原理分析

相關文章
相關標籤/搜索