📜 ⬆️ ⬇️

WebPack: how does Hot Reloading work inside

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:

  1. What is this ./entry ?
  2. What do and how do --hot and --inline differ ?
  3. What is this - module-bind ?
  4. 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:

  1. 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
  2. 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.
  3. 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
  4. 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:

 /* WEBPACK VAR INJECTION */}.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:



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


Source: https://habr.com/ru/post/270593/


All Articles