「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」
1. 开启 HMR
我们在 webpack.config.js 文件中添加 devServer.hot 的配置:
此外,通常情况下,我们在设置模块热替换时,建议同时设置 target 选项(告诉 webpack 为什么环境打包),不然有时候会出问题(虽然在 webpack 的官方文档中没有找到有关说明)。因为,我们当前项目是跑在 web 环境上的,所以我们将 target 配置为 web:
修改完 webpack 的配置后,我们需要重新执行 npm run serve,等到开启服务并成功编译完成后,我们再到浏览器中,刷新页面看下效果:
可以看到,模块热替换功能已经开启了,并且它正在等待来自 webpack-dev-server 的信号(信号来自于 webpack-dev-server 中提供的 socket 服务器,我们后面会说)。
那么我们现在修改 src/js/element.js 中的代码看下效果,我们把之前打印语句中的内容修改为“嘻嘻嘻”后保存修改,待重新编译完后观察浏览器中的变化:
通过观察你会发现,当我们修改了某一个模块(element.js)的代码时,浏览器依然是对整个页面进行了刷新,并没有展示出模块热替换的效果。事实上,这时并没有做模块热替换,因为 webpack 默认情况下不知道要对哪些模块做热替换,它认为任何模块发生了更新时都需要刷新浏览器。如果我们想要让某个模块开启热替换的功能,就需要去导入了该模块的文件中指定这个模块发生更新时,进行 HMR,比如我们在导入了 element.js 的 main.js 文件中添加相应的代码:
...
import './js/element'; // 之前导入模块的语句还是需要的,不能去掉
if (module.hot) { // 先判断一下全局的 module 对象上的 hot 有没有开启,如果开启了,再来指定要接受热替换的模块
// accept 的第一个参数是要开启热替换功能的模块,第二个参数是可选的回调函数,用来在监听到该模块热更新时,可以做一些额外的事情
module.hot.accept('./js/element.js', () => {
console.log('element 模块发生更新了');
});
}
...
然后,我们再来修改 element.js 中的代码,将打印语句的内容随便修改一下,看下效果:
可以看到,这次浏览器就没有自动刷新了,控制台中也是在保留了原来内容的基础上继续做的有关内容的打印(先是加载了热替换后的模块中的代码,之后又执行了回调函数中的内容)。
所以,现在我们就已经实现某个模块的热替换功能了,而如果有多个模块需要实现热替换,我们只要再通过 module.hot.accept() 函数一个个添加即可,比如我们再开启 ./js/math.js 模块的热替换功能:
...
import { sum } from './js/math';
...
import './js/element';
if (module.hot) {
module.hot.accept('./js/element.js', () => {
console.log('element 模块发生更新了');
});
module.hot.accept('./js/math', () => {
console.log('math 模块发生更新了');
});
}
...
但这样一个个来写有点麻烦,其实在真实开发中,我们是不需要这样一个一个去指定需要热替换的模块的,因为真实开发我们通常会使用框架,而框架会帮我们实现 HMR 的功能。