Webpack & The Hot Module Replacement

Webpack has a nifty feature called Hot Module Replacement [HMR] that helps replace old modules with the newer ones without reloading the browser. This is very helpful in many cases, for example when working with some dialog box or 3rd page of a navigation wizard and so on. In these cases you want to see the changes but reloading the browser to see the changes takes the app back to initial screen.

You may be using it already but may not know exactly how it works. So in this blog, I’ll go over how it works.

Things To Note About HMR

  1. HMR is one of Webpack’s optional features and needs to be turned ON explicitly.
  2. You need to use Webpack via webpack-dev-server, one of Webpack’s two interfaces (the other one is the CLI).
  3. It can only work with “loaders” that implement and understand HMR API. For example: “style-loader”, “react-hot-loader” etc.
  4. It should only be used for development because it adds a lot of extra code and secondly, webpack-dev-server is development-only server.

How It Works

Webpack injects a whole lot of JS libraries to make HMR work. The below picture shows how various parts work when a file/module change to make it happen.

Note: You can click on the picture to zoom and read

Legend:
Green — Webpack-dev-server related libs.
Blue — Webpack core and plugin libs.
Orange — chunk manifest or chunk JS file itself.
Red — React-loader or Style-loader HMR libs.
Teal — Your App
Purple — File or module that just changed.A JS or CSS file (module) changes
  1. Webpack uses HotModuleReplacementPlugin to generate a manifest(a JSON containing list of changed modules) and an update file(a JS with the actual change info).
  2. Webpack then tells webpack-dev-server about the changes
  3. webpack-dev-server tells “webpack-dev-server/client”(JS file) running in the browser via webSocket by sending “invalid” notification via WebSocket.
  4. “webpack-dev-server/client” then sends the initial hash(e.g. b2e2d54372f42c1b2352) that it got when the app was first loaded to hot/dev-server library. “hot/dev-server” acts like the main interface to all other JS files.
  5. “hot/dev-server” then calls internal libraries(like JSONP runtime) that are also injected by Webpack to download the manifest file.
  6. JSONP runtime uses hash and loads the manifest file from the webpack-dev-server.
  7. The manifest file contains details about chunks that need to be uploaded to the browser. It’s file name looks like: b2e2d54372f42c1b2352.hot-update.json and it’s content looks like this: {“h”:”b3632c2a800d437e17df”,”c”:[0]}
  8. JSONP runtime then uses information contained inside the manifest file to load all the “Update” files and adds them to the DOM.
  9. “Updates JS” themselves are JS files. They contain information about actual changes that needs to be applied. They are added to the DOM and executed.
//Example: 0.b2e2d54372f42c1b2352.hot-update.js chunk
//Notice that the chunk is actually a function call to "webpackHotUpdate" HMR function. So when the chunk is loaded to the DOM, this function instantly called with the Chunk's info.
//In this case, style-loader is telling HMR about a new CSS class:
//input { background: pink;} for module id "82"

webpackHotUpdate(0,
{
82:
function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(79)();
exports.push([module.id, “input {\n background: pink;\n}”, “”])
}
})

11. Updates call HMR runtime with the module id (e.g. moduleId “82” in the above example) and the actual changes.

12. But HMR runtime itself doesn’t know how to deal with the changes. So it delegates this job to corresponding loaders like react-hot-loader runtimes or style-loader runtimes to apply changes. (Note these are also injected into the Browser).

13. If there is no issues with applying the changes, the appropriate runtimes updates the module.

14. If there are issues (like syntax errors in the change), “hot/dev-server” lib is notified about the error. Note: if you are using “hot/dev-only-server”, an alternative to “hot/dev-server” then browser won’t reload.

A Deeper Look

Let’s take an example to see some of the above steps in details. Below is simple app that has a field and loads CSS (via css-loader and style-loader). The CSS currently changes body’s background color to gray.

With HMR working, if we change the background to green in the style.css file, HMR updates the app’s background to green without removing the text inside the input field (i.e. doesn’t reloads the browser and keeps the “state” as is).

0. Initial setup

Initially when you setup webpack-dev-server using — inline and — hot.

Note: You can click on the pictures to zoom and read

1. File Changed

Webpack along with HMR plugin generates a manifest file “<previousHash>.hot-update.json” and the update file “<cunkNumber>.<previousHash>.hot-update.js”.

Note the previousHash is the hash that’s generated when the app was first loaded and every time it’s patched. When ever there is a change to the modules, the “previousHash” is used to load new files and then a new hash is generated and set as “previousHash” and the cycle continues.

2. Update Client about the changes on the server

webpack-dev-server constantly communicates with the client about the changes via webSocket.

3. Upload changes

When webpack-dev-server receives “Invalid” notification via webSocket, it asks HMR libraries running in the client (browser) to load new files. The libraries use the current hash (e3472f4aac7404e2e5b2) and use it as part of the file name for manifest (e3472f4aac7404e2e5b2.hot-update.json) file and also for the changed file (0.e3472f4aac7404e2e5b2.hot-update.js). Note that the “0” is the chunk number.

websocket, manifest and update files

Below picture shows the contents of the manifest file

manifest w/ details about the changes

4. Apply Changes

Below picture shows contents of the update file. Notice that it’s calling “webpackHotUpdate” function. So when this JS file is added to the DOM, it passes the details like module id, “78” and raw changes like (“background: green”) to the HMR runtime. HMR Runtime then passes this info the correct loader (i.e. style-loader HMR runtime) that knows how to deal with this change.

final logs after changes are made

That’s it! 🙏

My Other Posts

Functional Programming

  1. JavaScript Is Turing Complete — Explained

ES6

  1. 5 JavaScript “Bad” Parts That Are Fixed In ES6
  2. Is “Class” In ES6 The New “Bad” Part?

WebPack

  1. Webpack — The Confusing Parts
  2. Webpack & Hot Module Replacement [HMR]
  3. Webpack’s HMR And React-Hot-Loader — The Missing Manual

Draft.js

  1. Why Draft.js And Why You Should Contribute
  2. How Draft.js Represents Rich Text Data

React And Redux :

  1. Step by Step Guide To Building React Redux Apps
  2. A Guide For Building A React Redux CRUD App (3-page app)
  3. Using Middlewares In React Redux Apps
  4. Adding A Robust Form Validation To React Redux Apps
  5. Securing React Redux Apps With JWT Tokens
  6. Handling Transactional Emails In React Redux Apps
  7. The Anatomy Of A React Redux App

Salesforce

  1. Developing React Redux Apps In Salesforce’s Visualforce

🎉🎉🎉 If you like this post, please 1. ❤❤❤ it below on Medium and 2. please share it on Twitter. You may retweet the below card🎉🎉🎉

Thanks for reading! 👍