Hot Module Replacement

This guide extends on code examples found in the Development guide.

       温馨提示:这个指南中的例子是基于上一个指南的。(╯°Д°)╯

Hot Module Replacement (or HMR) is one of the most useful features offered by webpack. It allows all kinds of modules to be updated at runtime without the need for a full refresh. This page focuses on implementation while the concepts page gives more details on how it works and why it's useful.

       Hot Module Replacement,或称之为HMR,是webpack提供的非常有用的特性之一。它允许你各种各样的模块在运行时自动更新,而无需整体更新。在这一趴,我们主要关注这一切是如何实现的,不像概念部分那样,我们会告诉你它是怎么工作的,为什么这样是实用的。

HMR is not intended for use in production, meaning it should only be used in development. See the building for production guide for more information.

       我们不建议在产品中使用HMR,这意味着它应当在开发过程中使用。戳这里,查看更多信息。

Enabling HMR

This feature is great for productivity. All we need to do is update our webpack-dev-server configuration, and use webpack's built in HMR plugin. We'll also remove the entry point for print.js as it will now be consumed by the index.js module.

       这个特性对于生产效率很重要。我们所需要做的全部的事情只是更新我们服务器的配置,只要使用HMR插件就行了。我们也将会移除print.js的入口点,由于它现在正在被index.js模块使用。

If you took the route of using webpack-dev-middleware instead of webpack-dev-server, please use the webpack-hot-middleware package to enable HMR on your custom server or application.

       如果你的技术路线是使用webpack-dev-middleware而不是webpack-dev-server,请使用webpack-hot-middleware来使得HMR在你的服务器或者程序中生效。

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');

  module.exports = {
    entry: {
-      app: './src/index.js',
-      print: './src/print.js'
+      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
+     hot: true
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
+     new webpack.NamedModulesPlugin(),
+     new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

You can use the CLI to modify the webpack-dev-server configuration with the following command: webpack-dev-server --hotOnly.

       你可以使用命令行来修改webpack-dev-server的配置,使用如下的命令:

webpack-dev-server --hotOnly

Note that we've also added the NamedModulesPlugin to make it easier to see which dependencies are being patched. To start, we'll get the dev server up and running by executing an npm start from the command line.

       请注意,我们还添加了这个功能NamedModulesPlugin,以便更轻松地查看哪些依赖关系正在进行修补。首先,我们将通过npm start从命令行执行一个启动并运行开发服务器。

Now let's update the index.js file so that when a change inside print.js is detected we tell webpack to accept the updated module.

       现在,让我们更新index.js,以便当print.js检测到内部更改时,我们告诉webpack接受更新的模块。

index.js

  import _ from 'lodash';
  import printMe from './print.js';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;

    element.appendChild(btn);

    return element;
  }

  document.body.appendChild(component());
+
+ if (module.hot) {
+   module.hot.accept('./print.js', function() {
+     console.log('Accepting the updated printMe module!');
+     printMe();
+   })
+ }

Start changing the console.log statement in print.js, and you should see the following output in the browser console.

       让我们开始改变print.js中的console.log语句,你应当能看到如下的在浏览器控制台的输出:

print.js

  export default function printMe() {
-   console.log('I get called from print.js!');
+   console.log('Updating print.js...')
  }

console

[HMR] Waiting for update signal from WDS...
main.js:4395 [WDS] Hot Module Replacement enabled.
+ 2main.js:4395 [WDS] App updated. Recompiling...
+ main.js:4395 [WDS] App hot update...
+ main.js:4330 [HMR] Checking for updates on the server...
+ main.js:10024 Accepting the updated printMe module!
+ 0.4b8ee77….hot-update.js:10 Updating print.js...
+ main.js:4330 [HMR] Updated modules:
+ main.js:4330 [HMR]  - 20
+ main.js:4330 [HMR] Consider using the NamedModulesPlugin for module names.

Via the Node.js API

When using Webpack Dev Server with the Node.js API, don't put the dev server options on the webpack config object. Instead, pass them as a second parameter upon creation. For example:

       当我们通过Node.js的API来使用Webpack Dev Server时,不要将dev server的选项配置在webpack的对象中。而是应该将这些配置信息作为构造函数的第二个参数传进去。举个例子哈:✧(≖ ◡ ≖✿

new WebpackDevServer(compiler, options)

To enable HMR, you also need to modify your webpack configuration object to include the HMR entry points. The webpack-dev-server package includes a method called addDevServerEntrypoints which you can use to do this. Here's a small example of how that might look:

       要启用HMR,还需要修改webpack配置对象以包含HMR入口点。该webpack-dev-server软件包包含一个addDevServerEntrypoints,您可以用来执行此操作的方法。下面是一个小例子,它可能是这样的:

dev-server.js

const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');

const config = require('./webpack.config.js');
const options = {
  contentBase: './dist',
  hot: true,
  host: 'localhost'
};

webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);

server.listen(5000, 'localhost', () => {
  console.log('dev server listening on port 5000');
});

If you're using webpack-dev-middleware, check out the webpack-hot-middleware package to enable HMR on your custom dev server.

       如果你正在使用webpack-dev-middleware,检查webpack-hot-middleware包,使得HMR可以应用在你的定制化的dev server中。

Gotchas

Hot Module Replacement can be tricky. To show this, let's go back to our working example. If you go ahead and click the button on the example page, you will realize the console is printing the old printMe function.

       模块热重置可能是棘手的。为了展现这个,让我们回到我们工作的例子。如果你单击我们例子的页面上的按钮,你将会意识到控制台正在打印旧的printMe函数的信息。

This is happening because the button's onclick event handler is still bound to the original printMe function.

       这是因为按钮的点击事件的解析器依旧绑定在原始的printMe的函数上。

To make this work with HMR we need to update that binding to the new printMe function using module.hot.accept:

       为了使得HMR起作用,我们需要使用module.hot.accept更新绑定,让它绑定到新的printMe函数:

index.js

  import _ from 'lodash';
  import printMe from './print.js';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;  // onclick event is bind to the original printMe function

    element.appendChild(btn);

    return element;
  }

- document.body.appendChild(component());
+ let element = component(); // Store the element to re-render on print.js changes
+ document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
-     printMe();
+     document.body.removeChild(element);
+     element = component(); // Re-render the "component" to update the click handler
+     document.body.appendChild(element);
    })
  }

This is just one example, but there are many others that can easily trip people up. Luckily, there are a lot of loaders out there (some of which are mentioned below) that will make hot module replacement much easier.

       这只是一个例子,但是这里有很多其他的情况会轻易地使得人们感到难缠。幸运的是,在这里有很多加载器(下面我们列举一些)可以使得模块热重置变得更加简易。( ゚∀゚)

HMR with Stylesheets (HMR和样式表)

Hot Module Replacement with CSS is actually fairly straightforward with the help of the style-loader. This loader uses module.hot.accept behind the scenes to patch <style> tags when CSS dependencies are updated.

       实际上,使用style-loader了之后,模块热重置来更新CSS就会相当简单。当CSS依赖发生更新时,该加载器使用module.hot.accept在后台修补<style>标记。

First let's install both loaders with the following command:

       首先,让我们用下面的命令安装这两个加载器:

npm install --save-dev style-loader css-loader

Now let's update the configuration file to make use of the loader.

       现在,让我们更新配置文件以使用加载器。

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const webpack = require('webpack');

  module.exports = {
    entry: {
      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
      hot: true
    },
+   module: {
+     rules: [
+       {
+         test: /\.css$/,
+         use: ['style-loader', 'css-loader']
+       }
+     ]
+   },
    plugins: [
      new CleanWebpackPlugin(['dist'])
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
      new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

Hot loading stylesheets is as easy as importing them into a module:

       我们只需要导入它们进模块之中,样式表就可以热加载了。

project

  webpack-demo
  | - package.json
  | - webpack.config.js
  | - /dist
    | - bundle.js
  | - /src
    | - index.js
    | - print.js
+   | - styles.css

styles.css

body {
  background: blue;
}

index.js

  import _ from 'lodash';
  import printMe from './print.js';
+ import './styles.css';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;  // onclick event is bind to the original printMe function

    element.appendChild(btn);

    return element;
  }

  let element = component();
  document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
      document.body.removeChild(element);
      element = component(); // Re-render the "component" to update the click handler
      document.body.appendChild(element);
    })
  }

Change the style on body to background: red; and you should immediately see the page's background color change without a full refresh.

       现在,改变body的样式为background: red;,然后你应当立即就能看到页面背景颜色的改变,而且不用整体刷新。

styles.css

  body {
-   background: blue;
+   background: red;
  }

Other Code and Frameworks

There are many other loaders and examples out in the community to make HMR interact smoothly with a variety of frameworks and libraries...

       在社区中还有很多其他的加载器和例子可以使HMR与各种框架和库进行顺畅的交互。

  • React Hot Loader: Tweak react components in real time.
  • Vue Loader: This loader supports HMR for vue components out of the box.
  • Elm Hot Loader: Supports HMR for the Elm programming language.
  • Redux HMR: No loader or plugin necessary! A simple change to your main store file is all that's required.
  • Angular HMR: No loader necessary! A simple change to your main NgModule file is all that's required to have full control over the HMR APIs.

P.S.:好了,7以后的部分就比较难懂了,我会在能力提高了再译,前六个指南以及基础概念学会,使用webpack就基本不会遇到什么难题了。


hengbao
22 声望2 粉丝

消极的心态限制潜能发挥