Skip to content
This repository was archived by the owner on Mar 22, 2019. It is now read-only.

hot module replacement with webpack.cn

e-cloud edited this page Mar 28, 2016 · 1 revision

Note that Hot Module Replacement (HMR) is still an experimental feature. 请注意,模块热更换(HMR)仍然是一个实验性的功能。

Introduction

简介

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.

在应用正在运行且不重载页面的前提下,模块热更换(HMR)对模块进行交换,添加或删除

Prerequirements

前置知识

How does it work?

它是怎么运行的呢?

Webpacks adds a small HMR runtime to the bundle, during the build process, that runs inside your app. When the build completes, Webpack does not exit but stays active, watching the source files for changes. If Webpack detects a source file change, it rebuilds only the changed module(s). Depending on the settings, Webpack will either send a signal to the HMR runtime, or the HMR runtime will poll webpack for changes. Either way, the changed module is sent to the HMR runtime which then tries to apply the hot update. First it checks whether the updated module can self-accept. If not, it checks those modules that have required the updated module. If these too do not accept the update, it bubbles up another level, to the modules that required the modules that required the changed module. This bubbling-up will continue until either the update is accepted, or the app entry point is reached, in which case the hot update fails.

在构建过程中,webpack给bundle注入了一个小型HMR runtime,以在应用启动时运行。当构建完成是,webpack并不会退出且处于待命状态,监视源文件的变更。每次webpack侦测出源码变更,它只会对变更的模块进行重新构建。根据设定,webpack会向HMR运行时发送一个信号,或者HMR运行时会轮询webpack源码的变更。无论哪种方式,改变的模块被发送到HMR运行时,然后尝试进行热更新。首先,它会检查更新的模块是否可以自我接收。如果不行,它会检查引用了更新模块的模块。如果这些都不支持更新,它会将事件冒泡到另一层面,就是引用了那些引用了更新模块的模块的模块。这种冒泡行为会持续到更新被接收,或者到达应用入口点因而热更新失败。

From the app view

从应用方面来看

The app code asks the HMR runtime to check for updates. The HMR runtime downloads the updates (async) and tells the app code that an update is available. The app code asks the HMR runtime to apply updates. The HMR runtime applies the update (sync). The app code may or may not require user interaction in this process (you decide).

应用程序代码会向HMR运行时检查更新。HMR运行时下载更新(异步),并告诉应用程序的代码有可用的更新。然后应用程序代码要求HMR运行时应用更新,HMR运行时(同步)应用更新。应用代码可以也可以不要求在此过程中需要用户交互(你来决定)。

From the compiler (webpack) view

从编译器方面来看

In addition to the normal assets, the compiler needs to emit the "Update" to allow updating the previous version to the current version. The "Update" contains two parts:

除了普通的资源,编译器需要发送“更新包”,以允许将旧版本代码换成新版本的。 “更新包”包含两部分:

  1. the update manifest (json)

  2. one or multiple update chunks (js)

  3. 更新清单(JSON)

  4. 一个或多个更新分块(JS)

The manifest contains the new compilation hash and a list of all update chunks (2.).

清单包含新编译的哈希码和所有更新分块的列表(见第二项)。

The update chunks contains code for all updated modules in this chunk (or a flag if a module was removed).

更新块包含全部更新模块(抑或是,一个标志,如果模块被删除的话)的代码。

The compiler also makes sure that module and chunk ids are consistent between these builds. It uses a "records" json file to store them between builds (or it stores them in memory).

编译器还需要确保模块和分块的ID在这些构建中保持一致。在构建时,它采用了一个“记录”JSON文件来存储这些信息(或者将它们存储在内存中)。

From the module view

从模块方面看

HMR is an opt-in feature, so it only affects modules that contain HMR code. The documentation describes the API that is available in modules. In general, the module developer writes handlers that are called when a dependency of this module is updated. He can also write a handler that is called when this module is updated.

HMR是一个可选功能,因此它只会影响包含HMR代码的模块。文档描述了在模块中可用的API。在一般情况下,模块开发者编写模块的依赖被更新时被调用的处理程序。他也可以写一个模块被更新时被调用的处理程序。

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 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的处理代码。如果一个模块没有HMR处理程序,则更新事件向上冒泡。这意味着单个处理程序可以处理一棵完整的模块树的更新。如果这棵树中有个模块更新了,整棵模块树会被重载(仅重载并不传送)。

From the HMR runtime view (technical)

从HMR运行时来看(技术关键)

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

对于模块系统,运行时是额外的注入代码,用来跟踪模块的parentschildren

On the management side the runtime supports two methods: check and apply.

在管理方面,运行时存在两种工作:checkapply

A check does an HTTP request to the update manifest. When this request fails, there is no update available. Otherwise 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 as update. The runtime switches into the ready state, meaning an update has been downloaded and is ready to be applied.

一次check发起一个HTTP请求以获取更新清单。若这个请求失败了,则没有可用的更新。否则更新块的列表会被与当前加载的块的列表进行比较。每一个已加载的块对应的更新块会被下载。所有模块的更新作为更新被运行时缓存。运行时切换到ready状态,意味着一个更新已被下载并准备加以应用。

For each new chunk request in the ready state the update chunk is also downloaded.

对于处于就绪状态请求的每个新块,更新块也被下载。[原文不太懂]

The apply method flags all updated modules as invalid. For each invalid module there needs to be a update handler in the module or update handlers in every parent. Else the invalid module bubbles up and marks all parents as invalid too. This process continues until no more "bubbling up" occurs. If it bubbles up from an entry point the process fails.

apply方法标志全部更新了的模块为无效。对于每一个无效的模块,需要模块或其父级有一个更新处理器。否则无效的模块冒泡,并标记所有的父母为无效了。这个过程一直继续,直到没有更多的“冒泡”发生。如果冒泡至一个入口点,则此过程中断。

Now all invalid modules are disposed (dispose handler) and unloaded. Then the current hash is updated and all "accept" handlers are called. The runtime switches back to the idle state and everything continues as normal.

现在,所有无效的模块都被抛弃(抛弃处理器)和卸载。然后当前的哈希码被更新,所有的“接收”的处理程序被调用。运行时切换回idle状态,然后一切都照常进行。

Generated files (technical)

生成的文件(技术关键)

The left side represents the initial compiler pass. The right side represents an additional pass with module 4 and 9 updated.

左边代表初始编译流程。右边表示模块4和9更新后的附加流程。

生成的更新块

What can I do with it?

我能做些什么呢?

You can use it in development as a replacement for LiveReload. Actually the webpack-dev-server supports a hot mode which tries to update with HMR before trying to reload the whole page. You only need to add the webpack/hot/dev-server entry point and call the dev-server with --hot.

您可以在开发中使用它作为LiveReload的替代品。其实webpack-dev-server支持热模式,此模式在试图重新加载整个页面之前尝试利用HMR更新。你只需要添加webpack/hot/dev-server到入口点,并使用--hot调用dev-server。

webpack/hot/dev-server will reload the entire page if the HMR update fails. If you want to reload the page on your own, you can add webpack/hot/only-dev-server to the entry point instead.

如果HMR更新失败,webpack/hot/dev-server会重载整个页面。如果你想自己刷新页面,那么你可以添加webpack/hot/only-dev-server到入口点

You can also use it in production as an updating mechanism. Here you would need to write your own management code that integrates HMR with your app.

您也可以在生产中使用其作为一个更新机制。不过你需要自己写一套管理代码来将HMR集成到你的应用。

Some loaders already generate modules that are hot-updateable (e.g. the style-loader can exchange a stylesheet). In these cases, you don't need to do anything special.

有些loader自行生成可热更新的模块(例如style-loader可以替换一个样式表)。在这种情况下,你不需要做什么特别的事情。

What is needed to use it?

使用需要什么?

A module can only be updated if you "accept" it. So you need to module.hot.accept the module in the parents or the parents of the parents. For example, a router or a subview would be a good place.

一个模块仅当你“接收”它才会被更新。所以你需要在模块的顶级父级设置module.hot.accept。例如,比较适合在一个路由器或者一个子视图设置。

If you only want to use it with the webpack-dev-server, just add webpack/hot/dev-server as entry point. Else you need some HMR management code that calls check and apply.

如果你只想结合webpack-dev-server使用,只需添加webpack/hot/dev-server为入口点。否则,你需要一些HMR管理代码来调用checkapply

You need to enable records in the Compiler to track module id between processes. (watch mode and the webpack-dev-server keep records in memory, so you don't need it for development)

您需要启用编译器记录功能以便在多次处理之间跟踪模块ID。 (监视模式和webpack-dev-server在内存中保存记录,所以你开发时并不需要开启)

You need to enable HMR in the Compiler to let it add the HMR runtime.

您需要对编译器启用HMR,让它注入HMR运行时。

What makes it so cool?

是什么让它这么diao

  • It's like LiveReload but for every module, so to speak.

  • You can use it in production.

  • The updates respect your Code Splitting and only download updates for the changed parts of your app.

  • You can use it for parts of your application and it doesn't affect other modules.

  • If HMR is disabled all HMR code is removed by the compiler (wrap it in if(module.hot))

  • 它就像LiveReload,但可以说是针对每一个模块的。

  • 可以在生产环境中使用它。

  • 更新逻辑遵从代码分割配置,只下载应用程序更改部分的更新。

  • 可以在应用程序的部分模块使用它,而不影响其他模块。

  • 如果HMR被禁用,所有HMR代码会被编译器移除(使用if(module.hot)包裹)

Caveats

##注意事项

  • It's experimental and not tested thoroughly.

  • Expect some bugs

  • Theoretically usable in production, but it maybe too early to use it for something serious

  • The module ids need to be tracked between compilations so you need to store them (records)

  • Optimizer cannot optimize module ids anymore after the first compilation. Therefore the bundle size is affected a little bit.

  • HMR runtime code increases bundle size.

  • For production usage additional testing is required to test the HMR handlers. This could be pretty difficult.

  • 它是实验性的和没有完全测试。

  • 预计会出现一些错误

  • 理论上可用在生产中,但为正经事而使用它也许过早。

  • 模块ID需要在多次编译之间进行跟踪,所以你需要存储它们(records

  • 第一次编译后,优化工具不能再优化模块ID。因此,是会影响一点点bundle的大小。

  • HMR运行时代码增加bundle的大小。

  • 若使用于生产环境,需要额外测试HMR处理程序。这可能相当困难。

Tutorial

教程

To use hot code replacement with webpack you need four things: 要WebPack使用代码热替换,你需要四样东西:

  • records (--records-path, recordsPath: ...)

  • globally enable hot code replacement (HotModuleReplacementPlugin)

  • hot replacement code in your code module.hot.accept

  • hot replacement management code in your code module.hot.check, module.hot.apply

  • 记录(--records-pathrecordsPath:...

  • 全局启用代码热替换(HotModuleReplacementPlugin

  • 代码中的热替换代码module.hot.accept

  • 代码中热替换管理代码module.hot.checkmodule.hot.apply

A small testcase:

/* style.css */
body {
	background: red;
}
/* entry.js */
require("./style.css");
document.write("<input type='text' />");

That's enough to use hot code replacement with the dev-server.

这足以结合dev-server使用代码热替换。

npm install webpack webpack-dev-server -g
npm install webpack css-loader style-loader
webpack-dev-server ./entry --hot --inline --module-bind "css=style\!css"

The dev server provides in-memory records, which is good for development. dev-server 支持常驻内存记录,方便开发。

The --hot switch enables hot code replacement.

--hot开关用来启用代码热替换

This adds the HotModuleReplacementPlugin. Make sure to use either the --hot flag, or the HotModuleReplacementPlugin in your webpack.config.js, but never both at the same time as in that case, the HMR plugin will actually be added twice, breaking the setup.

这里增加了HotModuleReplacementPlugin。请务必使用--hot标志,或webpack.config.js中的HotModuleReplacementPlugin,但不要同时使用。因为在那种情况下,HMR插件实际上被添加两次,会破坏webpack的正常运作。

There is special management code for the dev-server at webpack/hot/dev-server, which is automatically added by --inline. (You don't have to add it to your webpack.config.js)

webpack/hot/dev-server中有针对dev-server的专用管理代码,这是通过--inline自动添加的。(不用在webpack.config.js加入)

The style-loader already includes hot replacement code.

style-loader已经包括热替换的代码

If you visit http://localhost:8080/bundle you should see the page with a red background and a input box. Type some text into the input box and edit style.css to have another background color.

如果你访问http://localhost:8080/bundle,你应该带红色背景和一个输入框的页面。在输入框输入一些文字,并编辑style.css,改为另一种背景颜色。

Voilà... The background updates but without full page refresh. Text and selection in the input box should stay.

瞧...背景更新了,但没有刷新整个页面。在输入框中的文本和选择状态应该会保持。

Read more about how to write you own hot replacement (management) code: hot module replacement

更多关于如何写自己的热替换(管理)代码,参见:hot module replacement

Check the example-app for a demo without coding. (Note: It's a bit old, so don't look at the source code, because the HMR API changed a bit in between)

检出example-app来看看示例,而不必敲代码。(注:这个示例有点老了,所以不要看源代码,因为HMR的API后面改了一下下

Clone this wiki locally