Hot Module Replacement
自动刷新的问题
我们每次修改完代码,Webpack 都可以监视到变化,然后自动打包,再通知浏览器自动刷新,一旦页面整体刷新,那页面中的任何操作状态都将会丢失。
简单的应对方法:
- 回到代码中先写死一个文本内容到编辑器中,这样即便页面刷新,也不会丢失。
- 通过代码将编辑器中的内容及时保存到 LocalStorage 之类的地方,刷新过后再取回来。
这些方法不可谓不行,但都是有漏补漏、投机取巧的操作,并不能根治自动刷新导致页面状态丢失问题,而且这些方法都需要去编写一些与业务本身无关的辅助代码,不利于维护。
更好的办法自然是能够实现在页面不刷新的情况下,代码也可以及时的更新到浏览器的页面中,重新执行,避免页面状态丢失。针对这个需求,Webpack 同样可以满足。
模块热替换(HMR)
Webpack 中的模块热替换,指的是我们可以在应用运行过程中,实时的去替换掉应用中的某个模块,而应用的运行状态不会因此而改变。
自动刷新与 HMR 的区别:
- 通过自动刷新会导致整个应用的整体刷新,那页面中的状态信息都会丢失;
- 通过 HMR 可以实现只将修改的模块实时替换至应用中,不必完全刷新整个应用。
HMR 可以算是 Webpack 中最为强大的特性之一,而且也是最受欢迎的特性,因为它确实极大程度地提高了开发者的工作效率。
开启 HMR
HMR 已经集成在了 webpack 模块中了,所以不需要再单独安装什么模块。
使用这个特性最简单的方式就是,在运行 webpack-dev-server 命令时,通过 --hot 参数去开启这个特性。
或者也可以在配置文件中通过添加对应的配置来开启这个功能。那我们这里打开配置文件,这里需要配置两个地方:
- 首先需要将 devServer 对象中的 hot 属性设置为 true;
- 然后需要载入一个插件,这个插件是 webpack 内置的一个插件,所以我们先导入 webpack 模块,有了这个模块过后,这里使用的是一个叫作 HotModuleReplacementPlugin 的插件。
const webpack = require('webpack');
module.exports = {
// ...
devServer: {
// 开启 HMR 特性,如果资源不支持 HMR 会 fallback 到 live reloading
hot: true,
// 只使用 HMR,不会 fallback 到 live reloading
// hotOnly: true
},
plugins: [
// ...
// HMR 特性所需要的插件
new webpack.HotModuleReplacementPlugin(),
],
};
实际上,HMR 并非所有修改都会实时更新到页面且不自动刷新,尤其是运行脚本发生变化。
HMR 疑问
Q1:可能你会问,为什么我们开启 HMR 过后,样式文件的修改就可以直接热更新呢?我们好像也没有手动处理样式模块的更新啊?
A1:这是因为样式文件是经过 Loader 处理的,在 style-loader 中就已经自动处理了样式文件的热更新,所以就不需要我们额外手动去处理了。
Q2:那你可能会想,凭什么样式就可以自动处理,而我们的脚本就需要自己手动处理呢?
A2:这个原因也很简单,因为样式模块更新过后,只需要把更新后的 CSS 及时替换到页面中,它就可以覆盖掉之前的样式,从而实现更新。而我们所编写的 JavaScript 模块是没有任何规律的,你可能导出的是一个对象,也可能导出的是一个字符串,还可能导出的是一个函数,使用时也各不相同。所以 Webpack 面对这些毫无规律的 JS 模块,根本不知道该怎么处理更新后的模块,也就无法直接实现一个可以通用所有情况的模块替换方案。那这就是为什么样式文件可以直接热更新,而 JS 文件更新后页面还是回退到自动刷新的原因。
Q3:那可能还有一些平时使用 vue-cli 或者 create-react-app 这种框架脚手架工具的人会说,“我的项目就没有手动处理,JavaScript 代码照样可以热替换,也没你说的那么麻烦”。
A3:这是因为你使用的是框架,使用框架开发时,我们项目中的每个文件就有了规律,例如 React 中要求每个模块导出的必须是一个函数或者类,那这样就可以有通用的替换办法,所以这些工具内部都已经帮你实现了通用的替换操作,自然就不需要手动处理了。
如果你是使用 React 或者 Vue.js 这类的框架开发,那么使用 HMR 功能会更加简单,因为大部分框架都有成熟的 HMR 方案,你只需要使用就可以了。但是如果你是使用纯原生 JavaScript 开发,那 HMR 功能使用起来相对就会麻烦一点。这也正是为什么大部分人都喜欢选择集成式框架的原因。
关于框架的 HMR,因为在大多数情况下是开箱即用的,所以这里不做过多介绍,详细可以参考: