热模块更新 也就是我们常说的 HMR
假如我们现在index.js文件代码有如下功能,生成一个新增按钮,点击按钮的时候,生成item
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
这个时候,我们在index.js里引入一个
style.css
文件(别忘了配置解析css的loader)在
style.css
文件里,我们给偶数位的div设置背景色这样,我们点击新增后,会出现几个item,偶数的带背景
当我们修改
style.css
文件的时候,webpack-dev-server
会帮我们重新打包
,并重新刷新浏览器
,刷新后那我刚才点击生成的item都没有了
,那我要接着测试就需要重新点击按钮去新增item,这样会很麻烦
我们更换一下背景色,然后就可以看到,页面重新加载了localhost这个页面,很明显,这不是我们想要的效果
我希望的是,当我改变样式代码
的时候,不要帮我重新刷新页面,只要帮我替换掉样式代码就可以了,页面之前渲染出的元素不要动。为了达到这样的效果,该怎么配置呢?
- 配置
devServer
2.Hot Module Replacement
是webpack自带的插件,所以我们要先引入 webpack
const webpack = require('webpack');
-
然后配置plugins,到这一步,webpack的HMR功能就已经开启了,当我们改变了webpack配置后,一定要重启一下命令,重启后,我们再去修改css文件,页面上就只会替换样式,而JS渲染的元素不会变化
HMR的好处
- 可以在我们写CSS 的时候,方便我们调试CSS样式(上边的例子)
- 当JS引入了不同的模块,而当我们更新某一个文件时,不希望刷新整个页面,而只是在页面上变更具体模块,其他已经渲染的模块不受影响,看下边例子
如果我把HMR关闭
然后在 index.js里 引入两个模块,一个是counter.js,一个是number.js
counter.js显示在页面上初始值是1,而且当我点击数字的时候,会加1 ; number.js就单单显示一个数字1000.
这个时候,HMR是关闭的,运行'npm run start',页面上显示
然后,我点击第一个数字,让它不停加1
接着,我去修改number.js把1000变为2000
我们会发现,1000的确是变2000了,但是第一个数据也变为初始值了,我们在看network里的DOC,发现是页面重新请求了 localhost:8080,也就是说,页面刷新了
然后我们开启HMR,重启命令,当我们再次重复刚才的步骤,点击第一个数字让其变化,更改number.js数字后,发现,页面的确是不刷新了,第二个数据却没有发生变化。这个时候,需要我们再多加一段代码
加了这段代码后,我们再去重复刚才的步骤,会发现,页面上多了一个模块
这是因为,当number.js发生变化的时候,我们重新执行了一遍 number()方法却没有把页面上原来已经渲染出来的number模块删除掉,所以我们继续完善
然后我们就实现了 我们希望达到的效果,也就是 counter.js和number.js两个模块在页面上更新,不会刷新页面,不会互相影响,提升了我们的开发效率
其实,我们引入CSS代码,也是需要if(module.hot){...}
这样一段代码的,但是为什么我们没有写就实现了这样的功能呢?实际是因为,在css-loader
里,已经帮我们写好了,也就是css-loader
底层帮我们实现了这一段HMR的代码;我们平时用的vue也是有HMR的功能,但是我们也没写这样一段代码,是因为vue-loader
底层也帮我们内置了这段代码,所以就不用我们手写了。
- 如果我们引入了一些比较偏的文件类型,比如一些数据文件,而这些文件的loader里边,并没有内置HMR的效果,遇到这种文件,我们还是需要手动的去写下边这样一段代码的的
if (module.hot) { //如果当前的项目,开启了HMR的功能
module.hot.accept(' ', () => {
do something ....
})
}