This is the second day of my participation in the November Gwen Challenge. Check out the details: the last Gwen Challenge 2021

What is HMR

HMR (Hot Module Replacement) is an important feature of Webpack. When the code file is modified and saved, WebAPCK monitors the change of the file through watch. The code files are repackaged to generate two module patch files manifest (JS) and one (or more) updated chunks (js), the results are stored in the in-memory file system, and the repackaged modules are sent to the browser through the Websocket communication mechanism. The browser dynamically gets a new module patch to replace the old module, and the browser can update the application without refreshing the page.

Advantages:

  • Code file modification to page content update, done automatically
  • It is compatible with mainstream frameworks on the market: React, Vue, Angular2, for example, using Angular-CLI to create ng projects with @ngTools /webpack already built in Webpack
  • Compared to the location.reload() update, there is no need to refresh the page and the current state of the application can be saved

HMR related middleware

  1. webpack-dev-middleware

Essentially a container that passes webPack processed files to the server.

Webpack-dev-middleware is an express middleware with two core functions: first, it integrates node’s monery-FS /memfs internal file system through file-loader, and directly stores resources in memory; The second is to monitor file changes through Watch and compile dynamically.

2. webpack-hot-middleware

The core is to improve the communication mechanism between the server and the client for WebPack, internally using EventSocurce.

When WebPack is first packaged, in addition to the code itself, it also contains part of the HMRruntime subscription service code. Updates to the HMRruntime subscription service trigger the HMR Runtime API to pull the latest resource modules.

Webpack-hot-middleware enables hot reloading of pages.

3. webpack-dev-server

Webpack-dev-middleware and Express servers are built in, using Webpack-dev-middleware to listen to and compile files, using Express to provide HTTP services, The underlying layer uses Websocket instead of EventSource to implement the communication mechanism between the client and server provided by Webpack-hot-middleware.

EventSource

An EventSource is an interface to network events pushed by the server. An EventSource instance opens a persistent connection to the HTTP service, sends events in text/event-stream format, and remains open until asked to close.

Once the connection is opened, incoming messages from the server are distributed to your code as events. If there is an event field in the received message, the event is fired with the same value as the event field. If no event field exists, a generic event is emitted.

Unlike WebSockets, server-side push is one-way. Data information is distributed one-way from server to client. This makes them an excellent choice when there is no need to send data from the client to the server in the form of a message. For example, for handling social media status updates, news feeds, or for passing data to client-side storage mechanisms such as IndexedDB or Web storage, An EventSource can be an effective solution.

How HMR works

\

  1. Webpack — After watch starts listening mode, WebPack compiles the project for the first time and stores the results in the in-memory file system. Compared with disk file read and write mode, in-memory file management is faster. The in-memory Webpack server notifies the browser to load resources. The static resources obtained by the browser are in addition to the JS code content, as well as the HMR Runtime code injected by webpack-dev-server. Acts as a client for the browser to communicate with the Webpack server (Webpack-hot-middleware provides a similar function). Screenshot below:

  1. When a file (or module) changes in the file system, WebPack recompiles and packages the file, generates a unique hash value for each compilation, and generates two patch files based on the changed content: Description The manifest (file format is hash.hot-update.json, which contains hash and chundId to indicate the changed content) and chunk JS (hash.hot-update.js) modules for the changed content.

  1. Hrm-server pushes the manifest to the browser via websocket

The browser receives the latest hotCurrentHash, triggers the hotDownloadManifest function, and obtains the MANIFEST JSON file.

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

  1. The browser-side HMR Runtime pulls the latest chunk of the updated module using Ajax based on the hash and chunkId of the manifest
function hotDownloadUpdateChunk(chunkId) {
    var script = document.createElement("script");
    script.src = __webpack_require__.p + "" 
                     + chunkId + "." + hotCurrentHash + ".hot-update.js";	
    document.head.appendChild(script);
}
Copy the code

  1. Trigger the Render process to implement local thermal overloading

The HMR Runtime calls the window[“webpackHotUpdate”] method and hotAddUpdateChunk

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

HotAddUpdateChunk dynamically updates the code module and calls the hotUpdateDownloaded function

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(); }}Copy the code

HotUpdateDownloaded Performs hotApply performs hot overloading

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