模块热替换(HMR - Hot Module Replacement)作用:是指在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
webpack.config.js
// 在这里做打包配置
const path = require('path'); // 引入node的path模块(loader模块)
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 引入热更新插件,为webpack中自带的插件
const webpack = require('webpack');
// Common.js语法
module.exports = {
mode: 'development', // 默认模式为production,可不写,不写时打包运行命令行会有警告
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
port: 8080,
hot: true, // 让webpack-dev-server开启热更新的功能
hotOnly: true // 即便是hot配置(HMR)没有生效,也不让浏览器进行刷新,失效时不做其他处理
},
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin() // 实例化热更新插件
],
output: {
filename: 'dist.js', // 打包之后的输出文件
path: path.resolve(__dirname, 'dist')
}
}
// 配置完之后,一定要重新执行打包,不然配置项不会生效
// 样式文件变化,页面只修改展示样式,不会进行页面的刷新操作,也不会更改之前js操作的内容。
配置项的作用:
目录结构:
|--demo
|--node_modules
|--src
|--index.js
|--style.css
|--index.html
|--package.json
|--package-lock.json
|--webpack.config.js
src/style.css
div:nth-of-type(odd) {
background: #2086d7;
}
src/index.js
import './style.css';
var btn = document.createElement('button');
btn.innerHTML = '新增';
document.body.appendChild(btn);
btn.onclick = function() {
var div = document.createElement('div');
div.innerHTML = 'item';
document.body.appendChild(div);
}
执行打包:
npm run start
页面展示效果如图:(点击新增按钮,新增item项之后的效果)
此时,再去修改style.css里的样式:(把偶数行的背景色改为粉色)
div:nth-of-type(odd) {
background: pink;
}
页面展示效果为:(没有刷新浏览器,只更改了样式)
如果不开启热更新,修改样式之后,由于浏览器刷新,页面会恢复到初始状态,页面上只保留一个"新增"按钮,再去点击的时候,才会有不同颜色的item显示在页面中。
目录结构:
|--demo
|--node_modules
|--src
|--counter.js
|--number.js
|--index.js
|--index.html
|--package-lock.json
|--package.json
|--webpack.config.js
counter.js
function counter() {
var div = document.createElement('div');
div.setAttribute('id', 'counter');
div.innerHTML = 1;
div.onclick = function() {
div.innerHTML = parseInt(div.innerHTML, 10) + 1
}
document.body.appendChild(div);
}
export default counter;
number.js
function number() {
var div = document.createElement('div');
div.setAttribute('id', 'number');
div.innerHTML = 1000;
document.body.appendChild(div);
}
export default number;
index.js
import counter from './counter';
import number from './number';
counter();
number();
// 这里需要判断下是否开启了热更新,如果开启了,就只让number函数再执行一次,否则不会热更新
if(module.hot) {
module.hot.accept('./number', () => {
document.body.removeChild(document.getElementById('number'));
number();
})
}
本质上要实现HMR,都要写类似以上(判断是否开启热更新,单独执行某代码块)的代码,否则不会热更新。但在很多时候,我们开发过程中,并没有去写这样的代码,是因为在相应的loader中,已经帮我们实现了热更新的代码。如:
index.js中引入两个模块,如果不开启热更新,那么当一个模块里的数据变化了,就会导致页面刷新,使另一个模块内的数据也恢复到初始值,如果我们想一个模块里的js代码的变化,不影响另一个模块代码变更过的数据,每改一个js模块里的代码,只会更新当前模块的数据,不会影响其他js模块的数据。以上需求可借助HMR来实现,
执行打包:
npm run start
页面展示效果:(第一行数字带有点击事件,初始值为1,每点击一次数值会加1)
当点击数字值增加为7时,去修改number.js模块里的代码,把1000修改为3000,此时页面不刷新,counter.js模块里的数字7也没有变化,只更改了当前模块修改的数据。如下图:
这就是热更新的巧妙之处。? (如果不开启热更新,修改number.js里的代码后,由于页面会刷新,counter.js里的数字也会变成初始值1)