
Our voximplant platform actively uses javascript. With it, clients manage real-time calls, our backend logic and most of the frontend work on it. Javascript we love, appreciate and try to keep abreast of the latest news. Now our developers are actively experimenting with a promising bundle of webpack + typescript + react (by the way, for typescript we made
type definitions to our web sdk, but more on this some other time).
We especially like “hot module replacement”: the ability to change the source very quickly display changes in the browser without reloading the page. Looks like magic. Unfortunately, it is also documented as magic - according to the
eyeofhell , our technical evangelist, “the example at the office is a unique combination of special cases and special commands, any change in which makes it inoperable”. In our opinion, everything is not so bad, for a couple of evenings it is quite possible to figure it out. But not as easy as we would like. Therefore, especially for Habr under the cut, we will most simply and clearly explain how all this machinery works under the hood.
Magic as it is
The official example of using hot module replacement is very simple. The authors propose to create a
style.css file with one style:
body { background: red; }
')
And the
entry.js file, which uses the main feature of the webpack, is the
require command to add a
.css file to the page content. Well, it creates an element of type input, on which you can check the hot module replacement:
require("./style.css"); document.write("<input type='text' />");
It is further proposed to launch the webpack using the spell
webpack-dev-server ./entry --hot --inline --module-bind 'css=style!css'
And open the page available at
localhost: 8080 / bundle . Then you can observe hot module replacement magic: if you type some text in the input field, move the cursor to one of the symbols of this text, and then change the color in the
style.css file, then the background color of the page will change almost immediately, without losing it the entered text and even the cursor position will remain the same.
Good, comfortable magic. But after the start of use, many questions arise:
- What is this ./entry ?
- What do and how do --hot and --inline differ ?
- What is this - module-bind ?
- Why, if add react.js, hot reload stops working?
Exposing magic

Let's start with the simplest.
./entry and
--module-bind are cheating arguments that allow you to run webpack for demonstration purposes without the webpack.config.js configuration file. The first positional argument is just the name of the javascript file, which is the “entry point” to the program, it is his code that will be launched when the compiled bundle is executed. Many developers are confused by the fact that this argument does not look like a file name. This is actually the file name. Just to save characters, the authors of the example used one of the features of the webpack: the
require files and the command line can be specified without an extension, the webpack will automatically try to find such a
.js file (or with other extensions, if configured). The argument -
module-bind allows you to specify the used loaders without a configuration file, in this case, for files with the
css extension, the
css-loader will be used first and then the
style-loader loader . As you might guess, the suffix
-loader can also be omitted, and the authors of the example use this to save a few characters and entangle readers.
Operating mode "iframe automatic refresh" and built-in web server
In fact, webpack has three modes of automatic page refresh. The simplest mode is called
iframe mode : it turns on automatically if you run the webpack without the command line switches
--inline and
--hot , that is, like this:
webpack-dev-server ./entry --module-bind 'css=style!css'
The launched web server will give the following pages to the browser:
- localhost: 8080 / webpack-dev-server Shows a menu where you can see the source of the bundle created in memory or open a special html page in the browser, the only purpose of which is to run the javascript bundle code
- localhost: 8080 / webpack-dev-server / Differs from the previous link by the presence of a slash at the end. List of files in the folder where the server is running. Clicking the file will show it in the iframe and will automatically reload if the file changes.
- localhost: 8080 / webpack-dev-server / bundle The same page from the first paragraph. Opens in the iframe and automatically reboots. It will automatically reboot when changing any file that causes the bundle to be recompiled
- localhost: 8080 / and localhost: 8080 / bundle Inattention trap. Same as in the second and third paragraph, but the files and the bundle do not open in the iframe. It will not reboot. Why is she? For the second mode of operation, --inline . Why show in the first mode of operation? To confuse the developers, of course. Well, to distribute static without an iframe.
Operating mode "inline automatic refresh" and embedded refresh client
The second mode of operation is activated by the command key
–inline and is predictably called the “inline” mode. In this mode, everything is somewhat more complicated: the “refresh client” module is added to the bundle, the source code of which can be viewed in the
webpack-dev-server / client / index.js file . This module will be loaded using
require before your own code. Moreover, if you look at the generated bundle (using the web server menu, which I wrote about above), then you can see that this
require is not quite normal:
}.call(exports, "?http://localhost:8080"))
This is the result of executing the following code:
require("index?http://localhost:8080")
This poorly documented syntax "webpack resource query" allows you to pass arbitrary parameters to downloadable through require code. In this case, the webpack-dev-server generates the bundle, which, when the refresh client loads, passes the address of the webpack-dev-server running on the developer’s machine. Why does he need an address? Of course, to connect to the server via socketio and wait for notification of file changes. Having received such a notification, the refresh client will reload the page. Essentially the same thing happens with an iframe, but without an iframe. This allows you to debug the url-sensitive code and is used as an auxiliary mechanism for the third, most interesting mode of operation: hot module replacement
hot module replacement: a strong witch for quick development
As the attentive reader has already guessed, the third mode of operation is enabled by adding the command line key
--hot , which returns us to the spell that started this article. But here is not so simple. “Hot module replacement” is a webpack functionality, designed not only to quickly upload changes to the developer’s machine, but also to update sites in production. Using the
--hot bundle key will be compiled with support for hot module replacement: the corresponding code and
api are added to the webpack loader, HotModuleReplacementPlugin is responsible for this. The
--hot key understands both webpack-dev-server and webpack. With the help of hot module replacement api, a developer can query his server for “whether it was updated”, send the “update” command to the module tree and control how the modules are updated without reloading the page.
Here are two key points:
- The developer must write the code that learns about the update. Webpack considers module hashes and provides ajax api for downloading updates from the server - but the developer himself must call the module.hot.check method. It does not impose any way to communicate with the server and allows developers to integrate hmr into existing projects: you can learn about the availability of updates in any way, starting from the “check for updates” button with an ajax request and ending with a websocket connection from the page to the backend.
- Webpack does not update modules by itself. It gives modules the ability to subscribe to callback module.hot.accept , module.hot.decline and module.hot.dispose to respond to the update of their code received from the server. For example, the code of the module responsible for loading css, can apply the updated styles. And the code of the module that creates the ReactJS interface, call the new version of render () to redraw itself.
Given these two points, simply adding the code of the hot module replacement will do nothing — it will only increase the size of the bundle by a few kilobytes. We also need code that will communicate with the server, find out about the availability of updates and call
module.hot.check . And there is such a code!
webpack-dev-server , launched with the
--hot key, adds a “hot loader” module to the bundle being assembled, the source of which can be viewed in the
webpack / hot / dev-server.js file . This module, as well as the “refresh client” module, will be loaded before your code. It does an interesting thing: it subscribes to a dom event with the name
webpackHotUpdate and when receiving this event it uses the hot module replacement api to update the module tree. If the modules have not been updated (that is, the modules have either no update code or the code has returned the status of inability to update), then the hot loader reloads the whole page.
And who sends the
webpackHotUpdate event? This makes the "refresh client". The one added by the
--inline switch supports websocket connections to the
webpack-dev-server and monitors file changes. When using the
--hot key, the
webpack-dev-server sends the refresh client via the websocket a “hot” message that switches the refresh client to “hot mode”. In this mode, it stops updating the page itself, and instead sends the
webpackHotUpdate event.
Last question: where does the code that updates the CSS styles come from? As I wrote above, the webpack itself will not update anything and will simply call a callback to which the module can subscribe. Where does this callback come from? Surprise - the
style-loader has built-in support for the hot module replacement. Especially in order to work an example from the documentation.
findings
- If hot module replacement does not work - check that the correct mode is selected and that the used loaders support it. “Refresh client” and “hot loader” report to the log about what is happening.
- If instead of changing part of the page, it reloads entirely - also look in the log, there they will tell you which of the modules could not hot module replacement.
- The technology can be used not only when debugging on the developer's machine, for this you need to implement on the client and server side what webpack-dev-server does for you.
- Support for hot module replacement can be added to your modules and enjoy instant page refresh without reloading during development. The corresponding api is quite simple and well documented.