Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot Module Replacement Documentation #1251

Merged
merged 8 commits into from
Jun 3, 2017
136 changes: 136 additions & 0 deletions content/api/hot-module-replacement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
title: Hot Module Replacement
contributors:
- sokra
- skipjack
related:
- title: Concepts - Hot Module Replacement
url: /concepts/hot-module-replacement
- title: Guides - Hot Module Replacement
url: /guides/hot-module-replacement
---

If [Hot Module Replacement](/concepts/hot-module-replacement) has been enabled via the [`HotModuleReplacementPlugin`](/plugins/hot-module-replacement-plugin), it's interface will be exposed under the [`module.hot` property](/api/module-variables#module-hot-webpack-specific-). Typically, users will check to see if the interface is accessible, then begin working with it. As an example, here's how you might `accept` an updated module:

``` js
if (module.hot) {
module.hot.accept('./library.js', function() {
// Do something with the updated library module...
})
}
```

The following methods are supported...


### `accept`

Accept updates for the given `dependencies` and fire a `callback` to react to those updates.

``` js
module.hot.accept(
dependencies, // Either a string or an array of strings
callback // Function to fire when the dependencies are updated
)
```


### `decline`

Reject updates for the given `dependencies` forcing the update to fail with a `'decline'` code.

``` js
module.hot.decline(
dependencies // Either a string or an array of strings
)
```


### `dispose` (or `addDisposeHandler`)

Add a handler which is executed when the current module code is replaced. This should be used to destroy any persistent resource you have claimed or created. If you want to transfer state to the updated module, add it to given `data` parameter. This object will be available at `module.hot.data` after the update.

``` js
module.hot.dispose(data => {
// Clean up and pass data to the updated module...
})
```


### `removeDisposeHandler`

Remove the callback added via `dispose` or `addDisposeHandler`.

``` js
module.hot.removeDisposeHandler(callback)
```


### `status`

Retrieve the current status of the hot module replacement process.

``` js
module.hot.status() // Will return one of the following strings...
```

| Status | Description |
| ----------- | -------------------------------------------------------------------------------------- |
| idle | The process is waiting for a call to `check` (see below) |
| check | The process is checking for updates |
| watch | The process is in watch mode and will be automatically notified about changes |
| watch-delay | Delaying for a specified time after the initial change to allow for any other updates |
| prepare | The process is getting ready for the update (e.g. downloading the updated module) |
| ready | The update is prepared and available |
| dispose | The process is calling the `dispose` handlers on the modules that will be replaced |
| apply | The process is calling the `accept` handlers and re-executing self-accepted modules |
| abort | An update was aborted, but the system is still in it's previous state |
| fail | An update has thrown an exception and the system's state has been compromised |


### `check`

Test all loaded modules for updates and, if updates exist, `apply` them.

``` js
module.hot.check(autoApply, (error, outdatedModules) => {
// Catch errors and outdated modules...
})
```

The `autoApply` parameter can either be a boolean or `options` to pass to the `apply` method when called.


### `apply`

Continue the update process (as long as `module.hot.status() === 'ready').

``` js
module.hot.apply(options, (error, outdatedModules) => {
// Catch errors and outdated modules...
})
```

The optional `options` object can include the following properties:

- `ignoreUnaccepted` (boolean): Continue the update process even if some modules are not accepted.


### `addStatusHandler`

Register a function to listen for changes in `status`.

``` js
module.hot.addStatusHandler(status => {
// React to the current status...
})
```


### `removeStatusHandler`

Remove a registered status handler.

``` js
module.hot.removeStatusHandler(callback)
```
4 changes: 2 additions & 2 deletions content/api/module-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ if ( module.hot ) {
}
```

The compiler treats this as a split point and will split everything from `lodash` into a separate bundle that will be loaded as soon as the `loadLodash` function is called. See the [async code splitting guide](/guides/code-splitting-async) for more information.
The compiler treats this as a split point and will split everything from `lodash` into a separate bundle. This returns a promise that will resolve to the module once the bundle has been loaded. See the [async code splitting guide](/guides/code-splitting-async) for more information.



Expand Down Expand Up @@ -94,7 +94,7 @@ W> Using it asynchronously may not have the expected effect.
require.resolve(dependency: String)
```

Synchronously retrieve a module's ID. The compiler will ensure that the dependency is available in the output bundle. See [`module.id`](/api/module#module.id-commonjs) below.
Synchronously retrieve a module's ID. The compiler will ensure that the dependency is available in the output bundle. See [`module.id`](/api/module-variables#module-id-commonjs-) for more information.

``` javascript
var id = require.resolve("dependency");
Expand Down
2 changes: 1 addition & 1 deletion content/api/module-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This is `false` if the module is currently executing, and `true` if the sync exe

### `module.hot` (webpack-specific)

Indicates whether or not Hot Module Replacement is enabled. See [Hot Module Replacement](/concepts/hot-module-replacement).
Indicates whether or not [Hot Module Replacement](/concepts/hot-module-replacement) is enabled and provides an interface to the process. See the [HMR API page](/api/hot-module-replacement) for details.


### `module.id` (CommonJS)
Expand Down
102 changes: 37 additions & 65 deletions content/concepts/hot-module-replacement.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,72 @@
title: Hot Module Replacement
sort: 11
contributors:
- SpaceK33z
- sokra
- GRardB
- rouzbeh84
- SpaceK33z
- sokra
- GRardB
- rouzbeh84
- skipjack
---

Hot Module Replacement (HMR) exchanges, adds, or removes
[modules](/concepts/modules/) while an application is running without a
page reload. This allows you to speed up development time by updating
individual modules when they are changed without refreshing the page.
Hot Module Replacement (HMR) exchanges, adds, or removes [modules](/concepts/modules/) while an application is running, without a full reload. This can speed up significantly speed up development in a few ways:

- Retain application state which is lost during a full reload.
- Save valuable development time by only updating what's changed.
- Tweak styling faster -- almost comparable to changing styles in the browser's debugger.


## How It Works

### From The App View
Let's go through some different viewpoints to understand exactly how HMR works...

1. The app code asks the HMR runtime to check for updates.
2. The HMR runtime downloads the updates (asynchronously) and tells the app code that an update is available.
3. The app code then asks the HMR runtime to apply the updates.
4. The HMR runtime applies the update (synchronously).
### In the Application

You can set up HMR so that this process happens automatically, or you can
choose to require user interaction for updates to occur.
The following steps allow modules to be swapped in and out of an application:

1. The application asks the HMR runtime to check for updates.
2. The runtime asynchronously downloads the updates and notifies the application.
3. The application then asks the runtime to apply the updates.
4. The runtime synchronously applies the updates.

### From The Compiler (webpack) View
You can set up HMR so that this process happens automatically, or you can choose to require user interaction for updates to occur.

In addition to the normal assets, the compiler needs to emit an "update"
to allow updating from previous version to the new version. The "update"
consists of two parts:

1. The update manifest (JSON)
2. One or more update chunks (JavaScript)
### In the Compiler

The manifest contains the new compilation hash and a list of all update chunks.
In addition to normal assets, the compiler needs to emit an "update" to allow updating from previous version to the new version. The "update" consists of two parts:

Each update chunk contains code for all updated modules in the respective chunk
(or a flag indicating that the module was removed).
1. The updated [manifest](/concepts/manifest) (JSON)
2. One or more updated chunks (JavaScript)

The compiler makes sure that module IDs and chunk IDs are consistent
between these builds. It typically stores these IDs in memory (for example, when
using [webpack-dev-server](/configuration/dev-server/)), but it's also possible to
store them in a JSON file.
The manifest contains the new compilation hash and a list of all updated chunks. Each of these chunks contains the new code for all updated modules (or a flag indicating that the module was removed).

The compiler ensures that module IDs and chunk IDs are consistent between these builds. It typically stores these IDs in memory (e.g. with [webpack-dev-server](/configuration/dev-server/)), but it's also possible to store them in a JSON file.

### From The Module View

HMR is an opt-in feature that only affects modules containing HMR code. One example
would be patching styling through the [`style-loader`](https://github.com/webpack/style-loader).
In order for patching to work, `style-loader` implements the HMR interface; when it
receives an update through HMR, it replaces the old styles with the new ones.
### In a Module

Similarly, when implementing the HMR interface in a module, you can describe what should
happen when the module is updated. However, in most cases, it's not mandatory to write
HMR code in every module. If a module has no HMR handlers, the update bubbles up. This
means that a single handler can handle an update to a complete module tree. If a single
module in this tree is updated, the complete module tree is reloaded (only reloaded,
not transferred).
HMR is an opt-in feature that only affects modules containing HMR code. One example would be patching styling through the [`style-loader`](https://github.com/webpack/style-loader). In order for patching to work, the `style-loader` implements the HMR interface; when it receives an update through HMR, it replaces the old styles with the new ones.

Similarly, when implementing the HMR interface in a module, you can describe what should happen when the module is updated. However, in most cases, it's not mandatory to write HMR code in every module. If a module has no HMR handlers, the update bubbles up. This means that a single handler can update a complete module tree. If a single module from the tree is updated, the entire set of dependencies is reloaded.

### From The HMR Runtime View (Technical)
See the [HMR API page](/api/hot-module-replacement) for details on the `module.hot` interface.

For the module system runtime, additional code is emitted to track module `parents` and `children`.

On the management side, the runtime supports two methods: `check` and `apply`.
### In the Runtime

A `check` makes an HTTP request to the update manifest. If this request fails,
there is no update available. If it succeeds, the list of updated chunks is compared
to the list of currently loaded chunks. For each loaded chunk, the corresponding
update chunk is downloaded. All module updates are stored in the runtime.
When all update chunks have been downloaded and are ready to be applied, the runtime
switches into the `ready` state.
Here things get a bit more technical... if you're not interested in the internals, feel free to jump to the [HMR API page](/api/hot-module-replacement) or [HMR guide](/guides/hot-module-replacement).

The `apply` method flags all updated modules as invalid. For each invalid module,
there needs to be an update handler in the module or update handlers in its parent(s).
Otherwise, the invalid flag bubbles up and marks its parent(s) as invalid too. Each bubble
continues until the app's entry point or a module with an update handler is reached
(whichever comes first). If it bubbles up from an entry point, the process fails.
For the module system runtime, additional code is emitted to track module `parents` and `children`. On the management side, the runtime supports two methods: `check` and `apply`.

Afterwards, all invalid modules are disposed (via the dispose handler) and unloaded.
The current hash is then updated and all "accept" handlers are called. The runtime
switches back to the `idle` state and everything continues as normal.
A `check` makes an HTTP request to the update manifest. If this request fails, there is no update available. If it succeeds, the list of updated chunks is compared to the list of currently loaded chunks. For each loaded chunk, the corresponding update chunk is downloaded. All module updates are stored in the runtime. When all update chunks have been downloaded and are ready to be applied, the runtime switches into the `ready` state.

The `apply` method flags all updated modules as invalid. For each invalid module, there needs to be an update handler in the module or in its parent(s). Otherwise, the invalid flag bubbles up and invalidates parent(s) as well. Each bubble continues until the app's entry point or a module with an update handler is reached (whichever comes first). If it bubbles up from an entry point, the process fails.

## What It Can Be Used For
Afterwards, all invalid modules are disposed (via the dispose handler) and unloaded. The current hash is then updated and all `accept` handlers are called. The runtime switches back to the `idle` state and everything continues as normal.

You can use it in development as a LiveReload replacement.
[webpack-dev-server](/configuration/dev-server/) supports a
hot mode in which it tries to update with HMR before trying to reload the whole page. See how
to implement [HMR with React](/guides/hmr-react) as an example.

## Get Started

Some loaders already generate modules that are hot-updatable. For example, the `style-loader`
can swap out a page's stylesheets. For modules like this, you don't need to do anything special.
HMR can be used in development as a LiveReload replacement. [webpack-dev-server](/configuration/dev-server/) supports a `hot` mode in which it tries to update with HMR before trying to reload the whole page. See the [Hot Module Replacement guide](/guides/hot-module-replacement) for details.

webpack's power lies in its customizability, and there are *many* ways of configuring HMR
depending on the needs of a particular project.
T> As with many other features, webpack's power lies in its customizability. There are _many_ ways of configuring HMR depending on the needs of a particular project. However, for most purposes, `webpack-dev-server` is a good fit and will allow you to get started with HMR quickly.
2 changes: 1 addition & 1 deletion content/guides/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ The command above should automatically open your browser on `http://localhost:80

Make a change in one of your files and hit save. You should see that the console is recompiling. After that's done, the page should be refreshed. If nothing happens in the console, you may need to fiddle with [`watchOptions`](/configuration/dev-server#devserver-watchoptions-).

Now you have live reloading working, you can take it even a step further: Hot Module Replacement. This is an interface that makes it possible to swap modules **without a page refresh**. Find out how to [configure HMR](/guides/hmr-react).
Now you have live reloading working, you can take it even a step further: Hot Module Replacement. This is an interface that makes it possible to swap modules **without a page refresh**. See the [Hot Module Replacement guide](/guides/hot-module-replacement) for more information.

By default **inline mode** is used. This mode injects the client - needed for live reloading and showing build errors - in your bundle. With inline mode you will get build errors and warnings in your DevTools console.

Expand Down
Loading