How Does Webpack 4 Hot Module Replacement Work? A Deep Dive
This article explains Webpack 4’s hot module replacement feature, covering the compilation process, dev‑server setup, entry configuration, websocket communication, the role of HotModuleReplacementPlugin, and the detailed steps of detecting changes, downloading updates via JSONP, and applying updated modules in the browser.
Introduction
Webpack 4 provides a powerful Hot Module Replacement (HMR) feature that updates changed modules in the browser without a full page reload. This guide walks through the entire HMR workflow, from the initial compilation to the final module replacement in the client.
1. What Is Webpack Hot Update?
Hot update allows developers to save a file (Ctrl+S) and see the changes reflected instantly in the browser, eliminating the need for manual refreshes.
2. Webpack Compilation Process
When npm run dev is executed, Webpack starts a compilation and prints a hash identifier (e.g., b6db0705ddcf5a433075) that marks the build. After a file change, a new hash (e.g., 480ff6313164fc01043e) is generated, and two additional files appear: hot-update.js and hot-update.json. The hash in these files matches the previous build, linking the two compilations.
3. Webpack Hot Update Mechanism
3.1 webpack-dev-server
The dev server creates a compiler instance and starts an Express server with a WebSocket layer for bidirectional communication.
// node_modules/webpack-dev-server/bin/webpack-dev-server.js
let compiler = webpack(webpackOptions);
let server = new Server(compiler, options);
server.listen(options.port, options.host, (err) => {
if (err) throw err;
if (options.bonjour) broadcastZeroconf(options);
const uri = createDomain(options, server.listeningApp) + suffix;
reportReadiness(uri, options);
});3.2 Modifying webpack.config.js Entry
Before starting the server, server.addDevServerEntrypoints injects two client entries: the WebSocket client and the HMR runtime.
// lib/util/addDevServerEntrypoints.js
const clientEntry = `${require.resolve('../../client/')}` + `?${domain}${sockHost}${sockPath}${sockPort}`;
if (options.hotOnly) {
hotEntry = require.resolve('webpack/hot/only-dev-server');
} else if (options.hot) {
hotEntry = require.resolve('webpack/hot/dev-server');
}Resulting entry configuration:
{
entry: {
main: [
'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
'xxx/node_modules/webpack/hot/dev-server.js',
'./src/main.js'
]
}
}3.3 Listening for Compilation Completion
Webpack‑dev‑server taps into the compiler’s done hook and pushes the latest hash and any errors/warnings to connected browsers via WebSocket.
done.tap('webpack-dev-server', (stats) => {
this._sendStats(this.sockets, this.getStats(stats));
this._stats = stats;
});
_sendStats() {
this.sockWrite(sockets, 'hash', stats.hash);
if (stats.errors.length > 0) {
this.sockWrite(sockets, 'errors', stats.errors);
} else if (stats.warnings.length > 0) {
this.sockWrite(sockets, 'warnings', stats.warnings);
} else {
this.sockWrite(sockets, 'ok');
}
}3.4 File Watching
The server uses webpack-dev-middleware, which internally calls compiler.watch to monitor source files. Compiled assets are written to an in‑memory filesystem for fast access.
// node_modules/webpack-dev-middleware/index.js
compiler.watch(options.watchOptions, (err) => { /* error handling */ });
setFs(context, compiler); // writes bundles to memory‑fs3.5 Browser Receiving Update Notifications
The injected client script ( webpack-dev-server/client/index.js) opens a WebSocket connection and registers handlers for hash and ok events.
// webpack-dev-server/client/index.js
var socket = require('./socket');
var onSocketMessage = {
hash: function(_hash) { status.currentHash = _hash; },
ok: function() { sendMessage('Ok'); reloadApp(options, status); }
};
socket(socketUrl, onSocketMessage);When the ok event fires, reloadApp checks module.hot and emits a webpackHotUpdate event.
// webpack-dev-server/client/util/reloadApp.js
function reloadApp() {
if (hot) {
log.info('[WDS] App hot update...');
var hotEmitter = require('webpack/hot/emitter');
hotEmitter.emit('webpackHotUpdate', currentHash);
}
}3.6 HotModuleReplacementPlugin
The plugin injects runtime code that defines module.hot.check and handles the update lifecycle. It adds a hotCreateModule wrapper to each module when HMR is enabled.
3.7 module.hot.check Workflow
Requests hash-update.json via hotDownloadManifest using the previous hash.
Receives a list of changed modules and the next hash, then starts the preparation phase.
Subsequent JSONP request fetches hash.hot-update.js containing the new module code.
function hotDownloadUpdateChunk(chunkId) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.src = __webpack_require__.p + '' + chunkId + '.' + hotCurrentHash + '.hot-update.js';
head.appendChild(script);
}3.8 Applying the Update ( hotApply )
The core of HMR is the hotApply function, which removes outdated modules, inserts the new ones, and re‑executes the updated code.
var queue = outdatedModules.slice();
while (queue.length > 0) {
moduleId = queue.pop();
module = installedModules[moduleId];
delete outdatedDependencies[moduleId];
outdatedSelfAcceptedModules.push({ module: moduleId });
}
appliedUpdate[moduleId] = hotUpdate[moduleId];
for (moduleId in appliedUpdate) {
if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
modules[moduleId] = appliedUpdate[moduleId];
}
}
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
var item = outdatedSelfAcceptedModules[i];
moduleId = item.module;
try {
__webpack_require__(moduleId);
} catch (err) {
// error handling
}
}4. Summary
Webpack’s HMR works by tracking a compilation hash, notifying the browser via WebSocket, downloading updated chunks with JSONP, and finally swapping modules in place without a full reload. Understanding each step—from dev‑server initialization to the hotApply algorithm—helps developers debug issues and customize the update flow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
