3-9-10 Hot Module Replacement 热模块更新

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 后,如下:


3-9-10 Hot Module Replacement 热模块更新_第1张图片
image.png

连续,点击 add 按钮后,如下:


3-9-10 Hot Module Replacement 热模块更新_第2张图片
image.png

这时候,如果我们修改 css,将背景色改为 blue,如下:
/*index.css*/
div:nth-of-type(odd) {
    background: blue;
}
3-9-10 Hot Module Replacement 热模块更新_第3张图片
image.png

再次点击 add 按钮后,如下:


3-9-10 Hot Module Replacement 热模块更新_第4张图片
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);
}

点击上面一个数字后:


3-9-10 Hot Module Replacement 热模块更新_第5张图片
image.png

修改,number 的文案为 2000,如下:


3-9-10 Hot Module Replacement 热模块更新_第6张图片
image.png

之前的 js 操作没有了,也就是 number 模块的修改影响到了 counter 模块。
我们试一下配置 hot 和 hot-only,重新编译,发现,此时修改 number 时,


3-9-10 Hot Module Replacement 热模块更新_第7张图片
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 后,有:


3-9-10 Hot Module Replacement 热模块更新_第8张图片
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();
    });
}
3-9-10 Hot Module Replacement 热模块更新_第9张图片
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/

你可能感兴趣的:(3-9-10 Hot Module Replacement 热模块更新)