You, as a front-end leader, have worked with WebPack and are familiar with the hot update feature. If not, that’s fine.

What I want to talk about is my knowledge and understanding of Webpack hot update mechanism.

First of all:

What are hot updates?

Hot Module Replacement (HMR for short)

From the name interpretation, is to replace the “hot” module. Hot means the module is already running.

I wonder if you’ve ever heard or seen the phrase “Change your car engine to a 747 on the highway”.

It’s a little far-fetched, but it fits in here, in some ways.

Going a little further, let me tell you a little bit about a situation in my current job that many of you have encountered.

The development tool of wechat applet does not provide a mechanism similar to Webpack hot update, so in local development, every time the code is modified, the preview page will be refreshed, so the previous route jump state and the data filled in the form are gone.

Even a change in a copy or property configuration can result in a refresh, and getting back to a particular page and state can sometimes be cumbersome. This can be a waste of time in cases where you need to change your code frequently during development.

If there is a mechanism like Webpack hot update, the code is modified, not refreshed, but the existing data state is preserved, and only the module is updated and replaced. That is, the existing data state is preserved and the code changes are visible.

Very beautiful, but think about it is certainly not a simple thing.

So, what are hot updates?

To quote the official documentation, the hot update is:

A mechanism that allows an application to update, add, or remove modules while running without reloading

Hot updates solve the problem

So the problems that hot updates solve are also explained above. In my words, in the application development environment, it is convenient for developers to modify the code without refreshing the page, and intuitively see the mechanism of change on the page.

Simply put, it’s about improving development efficiency.

Thinking of my development experience in wechat small programs, I really feel that if there is a hot update mechanism, the development efficiency is much higher.

If you know that wechat mini program has or plans to support hot updates, or there are big guys who have done similar work, welcome to let me know, thank you!

Before going further, let’s take a look at how Webpack hot updates are configured.

Hot update configuration

If you’ve worked on a project where someone else built Webpack and configured hot updates, here’s how hot updates are configured.

My example uses Webpack 4. If you want to see the code directly, here it is:

Github.com/luobotang/w…

In addition to Webpack, you also need webpack-dev-server (or Webpack-dev-middleware).

To start hot updates for the Webpack development environment, do two things:

  • useHotModuleReplacementPluginThe plug-in
  • Open thewebpack-dev-serverThe hot update switch of

HotModuleReplacementPlugin plug-in is Webpack bring in Webpack. Config. Js joining is good:

// webpack.config.js
module.exports = {
  // ...
  plugins: [
    webpack.HotModuleReplacementPlugin(),
   // ...]}Copy the code

If you want to start the webpack development environment directly with webpack-dev-server, you can turn on the hot update switch of webpack-dev-server like this:

// webpack.config.js
module.exports = {
  // ...
  devServer: {
    hot: true.// ...}}Copy the code

It’s easy.

Hot Update Example

Here’s an example to further explain the hot update mechanism. If your previous experience with Webpack hot updates was provided to you by Vue via vue-loader, you have never written or seen anything like this in your own code:

if (module.hot) {
  module.hot.accept(/ *... * /)
  // ...
}
Copy the code

With this code, the following example is a good one to look at.

The examples are in webpack-hMR-demo above, but if you’re more comfortable with the code, you can go straight to webpack-HMR-demo.

Example 1: No hot updates

This example just gives you a glimpse of what the sample page can do and gives you a taste of the pain of having to refresh the page every time you change your code.

There is only one element on the page to display values:

<div id="root" class="number"></div>
Copy the code

The entry module (index.js) references two modules:

  • Timer.js: provides only a start interface, passing in the callback function, and the timer calls the callback function at intervals, passing in a number that increases each time
  • Foo. js: it doesn’t have much functionality, it simply exposes a message, which is introduced to distinguish timer.js from displaying different module update methods

The function of the entry module is simple: call timer.start(), and the incoming callback will update the value to the page each time:

import { start } from './timer'
import { message } from './foo'

var current = 0
var root = document.getElementById('root')
start(onUpdate, current)

console.log(message)

function onUpdate(i) {
  current = i
  root.textContent = The '#' + i
}
Copy the code

When the project is run, the page is constantly refreshing to show the increased value, something like this:

Whenever you change the code of any module, such as changing the interval of the timer in the timer (e.g., from 1 to 3 seconds), or the content displayed in onUpdate (e.g., ‘#’ + I to ‘*’ + I), the page is refreshed, the existing state is cleared, and the count starts from 0 again.

Example 2: Handling hot updates for dependent modules

The following example shows how updates to other modules are handled in index.js.

When a dependent module updates, it either accepts the change (the page doesn’t need to be refreshed, the module should be replaced) or it doesn’t accept the change (it must be refreshed).

Webpack exposes the hot update interface to the module as module.hot. Before using it, it is best to determine whether the current environment supports hot updates, as seen above:

if (module.hot) {
  // ...
}
Copy the code

Continuing with the previous example, select to accept and process the timer update, but not for the foo module:

if (module.hot) {
  module.hot.accept('timer', () => {
    // ...
  })
  module.hot.decline('./foo')
}
Copy the code

So, in the hot update mechanism, Webpack is told in this “declarative” way which module updates are processed and which module updates are not. Of course, updates to the module to be processed are handled in the callback function of the second argument to module.hot.accept(), which is executed after the declared module has been replaced.

Let’s look at the handling of the timer module update.

After calling the start function of the timer module, it returns a stop function that can terminate the timer. With this function, we can clean up the old timer module and re-call the start function of the new timer module based on the current state:

var stop = start(onUpdate, current) // Record the return stop function

// ...

if (module.hot) {
  module.hot.accept('timer', () => {
    stop()
    stop = start(onUpdate, current)
  })
  // ...
}
Copy the code

The processing logic is described above, stopping the timer for the old module with the previously recorded stop, then calling start for the new module to continue counting, passing in the current value so that the recount does not have to start from zero.

It looks pretty simple. The effect is that if you change the timer interval in the timer, the effect is immediately visible on the page, and the page does not refresh to restart the count from 0:

After running for a few seconds, change the timer interval in the timer module to 100ms

Modify message in Foo, and the page will refresh again.

A few extra notes:

  • If the timer module does not return the start interface after modification, the above processing mechanism will obviously be invalid. Therefore, the processing here is based on the condition that the interface of the module remains unchanged
  • The timer module’s start call must obviously return a stop function, otherwise there is no way to clear the started timer in the index.js module, which is also important
  • You may have noticed that the reference to the start function of the timer module seems to remain the same, so why is the start function in the callback the new module? This is actually handled by Webpack at compile time, the compiled code is not the current style, the start will be replaced, so that the start in the callback must refer to the new timer module start. Take a look at the Webpack documentation for a description of this.

In addition to declaring the processing of other modules’ updates, modules can also declare the processing of their own updates, which is the same interface, without passing an argument:

  • module.hot.accept()Tell Webpack that the current module update should not be refreshed
  • module.hot.decline()Tell Webpack to refresh when the current module is updated

Also, different modules that depend on the same module can have different declarations, which may be conflicting, for example, some allow dependent module updates and some do not. How does Webpack reconcile these declarations?

The implementation mechanism of Webpack is somewhat similar to the bubbling mechanism of DOM events. Update events are handled by the module itself. If the module itself does not have any declaration, it will bubble up to check whether the user has any declaration for the module update, and so on. If the final entry module has no declaration either, the page is refreshed. This is why in the previous example, even though hot updates were enabled, the page was still refreshed after the module changed, because there were no modules to handle the updates.

Example 3: Handling hot updates for its own module

The update process for its own modules is similar to that for dependent modules, and is declared to Webpack via the module.hot interface. However, the module itself may need to be updated before the module is replaced by Webpack. The updated processing is not done through the special interface, but directly written into the new module code.

Module.hot.dispose () is used to register the handler before the current module is replaced, and the callback receives a data object to which it can write data that needs to be saved, so that it can be retrieved from module.hot.data when the new module is executed:

var current = 0
if (module.hot && module.hot.data) {
  current = module.hot.data.current
}
Copy the code

First, when the module is executed, it checks whether there is any data left by the old module. If there is, it restores the data.

Then perform the processing before the module is replaced, which is to record the data and stop the existing timer:

if (module.hot)
  module.hot.accept()
  module.hot.dispose(data= > {
    data.current = current
    stop()
  })
}
Copy the code

After doing this, modify the onUpdate of index.js so that the values rendered to the page change without refreshing:

Change the ‘#’ + I in onUpdate() to ‘*’ + I after a few seconds of running

conclusion

So, having seen the example above, let’s summarize.

The hot update to Webpack is really just a set of interfaces and basic module replacement implementations. As a developer, you need to declare to Webpack in your code whether the dependency module and the current module can be updated and what happens before and after the update through the overheat update interface (module.hot.xxx).

If updates are accepted, it is up to the developers themselves to clean up or retain the necessary data and state before the module is replaced, and to restore the previous data and state after the module is replaced.

Of course, plugins like vue-loder already do this for us when we develop with Vue or React, and the *. Vue files need to be handled when they are updated. Many details are only known inside vue-loader.

But it would be nice to have a deeper understanding of how Webpack hot updates work, as I’ve seen colleagues working on the DOM themselves in Vue components (in order to encapsulate a component that directly manipulates the DOM) and have some problems with state clearance due to hot updates.

Only the developer can handle this, vue-Loader cannot handle this special case. At least know how to use Webpack’s hot update interface, in which case the developer is on his own.

In this paper, the introduction of Webpack hot update mechanism is only at the level of interface use, or the general mechanism, without in-depth explanation of the implementation principle and details of hot update. Time and space are limited, so let’s put a picture out first. Maybe we can talk about it in detail when we have time.

Source:

Webpack & The Hot Module Replacement medium.com/@rajaraodv/…

This article gives an in-depth introduction to the implementation principle of Webpack hot update.