webpack热更新HMR的使用
前言
虽然我们通过webpack-dev-server可以实现修改代码后的自动刷新,但还是有一些不如人意的地方。例如我们在页面中输入一段内容,希望对这段进行样式修改,然而我们修改样式后webpack-dev-server自动刷新了,我们之前输入的内容也被刷新清除掉了,而HMR就是用来解决这样的问题的。
一、HMR初体验
HMR ==》 Hot Module Replacement
HMR是配合webpack-dev-server来使用的,所以别忘了安装webpack-dev-server。
启用HMR也很简单,在webpack.config.js中配置devserver中的hot属性和添加一个webpack内置的HotModuleReplacementPlugin插件。
const webpack = require("webpack")
module.exports = {
...,
devServer:{
hot: true
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]
}
启动服务器看看效果,yarn webpack serve
,通过webpack5.X需要通过webpack的serve命令运行webpack-dev-server。
先试试修改css样式的效果。
css样式能够直接修改而不会改变现有状态。
再试试修改js内容:
添加了一段console.log()代码后,页面更新,但之前的状态也没保存下来。
这是因为webpack中的HMR需要手动处理模块热替换逻辑。而样式不需要手动处理是因为样式文件经过loader处理了,我配置了css-loader和style-loader处理css文件,在style-loader中会自动处理样式文件的热更新。
为什么样式可以自动处理,js就不行?因为样式模块更新过后,只需要把更新后的css及时替换到页面中,就可以覆盖之前的样式,从而实现样式的更新;
而我们所编写的js是没有任何规律的,我们可能导出一个对象、数组、字符串,我们对导出成员的使用也是各不相同的,所以webpack对这些毫无规律的js模块,就不知道如何去处理更新的模块,也就没有办法去帮我们实现一个可以通用所有情况的模块替换方案。
但是像使用了vue-cli/create-react-app这类脚手架的项目实现热更新不需要手动处理js模块的更新,也能实现热更新,这是因为使用的是框架,使用框架开发时,项目中的每个文件都有了规律,因为框架提供的就是一些规则,通过脚手架创建的项目内部集成了HMR方案。
总结:我们需要手动处理js模块更新后的热替换。
二、手动处理模块更新后的热替换逻辑
我们需要通过HMR的api来处理模块更新后的热替换,这些处理逻辑都写在我们的入口文件中。
因为打包从入口文件开始,也就是在这个文件开始去加载其他模块,就是因为这个文件中使用了导入的模块,一旦当这些模块更新了,就需要去重新使用这些模块,所以我们需要在这里去处理模块更新后的热替换。
在HMR的api中,为module对象提供了一个hot属性,这个属性也是一个对象,这也是HMR的核心对象,它提供了一个accept方法,用于去注册当某一个模块更新后的处理函数,这个方法第一个参数接收的是依赖模块的路径,第二个参数是依赖路径更新后的处理函数。
// 引入模块
import createEditor from "./editor.js"
const editor = createEditor()
document.body.append(editor)
// 定义lastEditor接收最后的editor组件
let lastEditor = editor
module.hot.accept('./editor.js', () => {
// console.log('editor.js模块更新了');
const val = lastEditor.innerHTML // 获取内容
// console.log("lastEditor.innerHTML", val);
document.body.removeChild(lastEditor) // 移除旧的组件
const newEditor = createEditor() // 创建新组件
newEditor.innerHTML = val // 将之前的内容放进去
document.body.append(newEditor) // 添加到DOM中
lastEditor = newEditor // lastEditor接收最新的组件,方便下次更新处理
})
// editor.js 内容
import './editor.css'
export default () => {
const editor = document.createElement("div")
editor.className = "div"
editor.contentEditable = true
console.log('已经测试过了');
return editor;
}
效果如下:
现在就能在修改editor.js里的内容并且实现热更新了,但只限于editor.js中,如果想要在其他组件的js中实现,需要单独去处理热更新逻辑。
三、HMR注意事项
- 处理HMR的代码报错会导致自动刷新(解决办法,使用hotOnly,就是将配置中的hot:true改成hotOnly:true)
- 没启用HMR的情况下,使用HMR api会报错,即如果没配置new webpack.HotModuleReplacementPlugin()和hot属性的话,使用module.hot.accept会报错(解决办法:加上一个if(module.hot)判断)
- 代码中多了一些与业务无关的代码(在将hot与webpack.HotModuleReplacementPlugin注释掉后,执行打包,可以发现打包入口文件中的热更新处理代码都被处理掉了。这里处理掉代码的前提是加上了第2点中的if判断)