webpack 熱更新(HMR)實現原理

HMR(Hot Module Replacement)是webpack一個重要的特性,當代碼文件修改並保存以後,webapck經過watch監聽到文件發生變化,會對代碼文件從新打包生成兩個模塊補丁文件manifest(js)和一個(或多個)updated chunk(js),將結果存儲在內存文件系統中,經過websocket通訊機制將從新打包的模塊發送到瀏覽器端,瀏覽器動態的獲取新的模塊補丁替換舊的模塊,瀏覽器不須要刷新頁面就能夠實現應用的更新。javascript

優勢:

  • 代碼文件修改到頁面內容更新,自動完成
  • 兼容目前市面上主流的開發框架 :react,vue,angular2,如使用angular-cli建立ng項目經過@ngtools/webpack已經內置了webpack
  • 相比location.reload() 更新方式,不須要刷新頁面,能夠保存應用的當前狀態

HMR相關的中間件

  • webpack-dev-middleware

本質上是一個容器,將webpack處理後的文件傳遞個服務器。前端

webpack-dev-middleware 是一個 express 中間件,核心實現兩個功能:第一經過 file-loader 內部集成了node的 monery-fs/memfs 內部文件系統,,直接將資源存儲在內存;第二是經過watch監聽文件的變化,動態編譯。vue

  • webpack-hot-middleware

核心是給webpack提升服務端和客戶端之間的通訊機制,內部使用windoe.EventSocurce實現。java

在webpack第一次打包的時候,除了代碼自己以外,還包含一部分HMRruntime訂閱服務代碼,HMRruntime 訂閱服務端的更新變化,觸發HMR runtime API拉取最新的資源模塊。node

webpack-hot-middleware實現頁面的熱重載。react

  • webpack-dev-server

內置了webpack-dev-middleware和express服務器,利用webpack-dev-middleware提供文件的監聽和編譯,利用express提供http服務,底層利用websocket代替EventSource實現了webpack-hot-middleware提供的客戶端和服務器之間的通訊機制。webpack

HMR的工做原理

  1. webpack --watch 啓動監聽模式以後,webpack第一次編譯項目,並將結果存儲在內存文件系統,相比較磁盤文件讀寫方式內存文件管理速度更快,內存webpack服務器通知瀏覽器加載資源,瀏覽器獲取的靜態資源除了JS code內容以外,還有一部分經過 webpack-dev-server 注入的的 HMR runtime代碼,做爲瀏覽器和webpack服務器通訊的客戶端( webpack-hot-middleware 提供相似的功能)。截圖以下:

  1. 文件系統中一個文件(或者模塊)發生變化,webpack監聽到文件變化對文件從新編譯打包,每次編譯生成惟一的hash值,根據變化的內容生成兩個補丁文件:說明變化內容的manifest(文件格式是hash.hot-update.json,包含了hash和chundId用來講明變化的內容)和chunk js(hash.hot-update.js)模塊。

  1. hrm-server經過websocket將manifest推送給瀏覽器

瀏覽器接受到最新的 hotCurrentHash,觸發 hotDownloadManifest 函數,獲取manifest json 文件。web

function hotDownloadManifest() {
    var request = new XMLHttpRequest();
    var requestPath = __webpack_require__.p + "" + hotCurrentHash + ".hot-update.json";
    request.open("GET", requestPath, true);        
    request.send(null);
}

image

  1. 瀏覽器端hmr runtime根據manifest的hash和chunkId使用ajax拉取最新的更新模塊chunk
function hotDownloadUpdateChunk(chunkId) {
    var script = document.createElement("script");
    script.src = __webpack_require__.p + "" 
                     + chunkId + "." + hotCurrentHash + ".hot-update.js";    
    document.head.appendChild(script);
}

  1. 觸發render流程實現局部熱重載

HMR runtime 調用window["webpackHotUpdate"] 方法,調用hotAddUpdateChunkajax

var parentHotUpdateCallback = window["webpackHotUpdate"];
window["webpackHotUpdate"] = function webpackHotUpdateCallback(chunkId, moreModules) {
    hotAddUpdateChunk(chunkId, moreModules);
    if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
};

hotAddUpdateChunk動態的更新代碼模塊,並調用hotUpdateDownloaded函數express

function hotAddUpdateChunk(chunkId, moreModules) {
    hotRequestedFilesMap[chunkId] = false;
    for (var moduleId in moreModules) {
        if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
            hotUpdate[moduleId] = moreModules[moduleId];             }
    }
    if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
        hotUpdateDownloaded();
    }
}

hotUpdateDownloaded執行hotApply執行熱重載

function hotUpdateDownloaded() {
    hotSetStatus("ready");
    Promise.resolve()
        .then(function() {
            return hotApply(hotApplyOnUpdate);
        })
}

參考

相關文章
相關標籤/搜索