1. 简介
模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。
2. HMR - CSS
关于 HMR 的使用场景,我们来看一个简单的示例。
// index.js
import './index.css';
var btn = document.createElement('button');
btn.innerText = 'add';
document.body.appendChild(btn);
btn.onclick = function() {
var div = document.createElement('div');
div.innerText = 'item';
document.body.appendChild(div);
};
/*index.css*/
div:nth-of-type(odd) {
background: yellow;
}
npm start 后,如下:
image.png
连续,点击 add 按钮后,如下:
image.png
这时候,如果我们修改 css,将背景色改为 blue,如下:
/*index.css*/
div:nth-of-type(odd) {
background: blue;
}
image.png
再次点击 add 按钮后,如下:
image.png
可以看到,我们修改 css 文件时,由于代码变动,重新编译并刷新了网页。导致之前的 js 操作都消失了,有没有变法只展示我们变动的 css 呢?答案是可以的。如下配置:
const webpack = require('webpack');
...
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
open: true,
port: 3000,
hot: true, // 开启热更新
hotOnly: true // 热更新失败时不刷新页面
},
...
plugins: [new HtmlWebpackPlugin({
template: "./src/index.html"
}), new webpack.HotModuleReplacementPlugin()]
重新编译,发现修改在即时生效的同时,保留了 js 操作,实现了 css 的热更新。
3. HMR - js
关于 js 模块的热更新该如何实现呢,我们来看一下。
首先去掉 hot 和 hot-only 配置。
增加一个 counter.js 文件和一个 number.js 文件,并引入:
// index.js
import Counter from './counter';
import Number from './number';
new Counter();
new Number();
new Number();
···
// counter.js
export default function() {
var div = document.createElement('div');
div.innerText = '1';
div.onclick = function() {
div.innerText = (parseInt(div.innerText) + 1) + '';
};
document.body.appendChild(div);
};
···
// number.js
export default function Header() {
var number = document.createElement('div');
number.innerText = '1000';
number.id = 'number';
document.body.appendChild(number);
}
点击上面一个数字后:
image.png
修改,number 的文案为 2000,如下:
image.png
之前的 js 操作没有了,也就是 number 模块的修改影响到了 counter 模块。
我们试一下配置 hot 和 hot-only,重新编译,发现,此时修改 number 时,
image.png
number 没有被更新,这是因为依赖模块更新时,我们需要主动对更新做出响应。
如下:
// index.js
import Counter from './counter';
import Number from './number';
new Counter();
new Number();
if (module.hot) {
module.hot.accept('./number', function() {
new Number();
});
}
此时,修改 number 后,有:
image.png
可以看到,webpack 并不会帮我们把原 number 干掉,然后在原位置更新一个新的 number,这些逻辑都要用户主动去实现。
// index.js
import Counter from './counter';
import Number from './number';
new Counter();
new Number();
if (module.hot) {
module.hot.accept('./number', function() {
document.body.removeChild(document.getElementById('number'));
new Number();
});
}
image.png
4. 小结
为何 js 实现 HMR 要用户手动实现更新逻辑,但是样式更新不需要呢?其实样式更新也是需要实现这个更新逻辑的,只不过 style-loader 实现了 HMR 接口,当它通过 HMR 接收到更新时,它会使用新的样式替换旧的样式。而 Vue 通过 vue-loader,react 通过 babel-preset 都在底层实现了该接口,用户无需关注。
参考
https://www.webpackjs.com/concepts/hot-module-replacement/
https://www.webpackjs.com/plugins/hot-module-replacement-plugin/
https://www.webpackjs.com/api/hot-module-replacement/
网友评论