webpack中的热更新

2021-12-18 16:13发布

自动刷新和热更新

项目自动刷新并不是在Webpack中才提出的,在之前WebStrom等开发工具就已经借助了浏览器提供的接口实现页面自动刷新。除了利用浏览器接口实现,还有向开发的网页中注入代理客户端,通过代理客户端去刷新整个页面。还可以把开发的网页装进一个iframe中,通过刷新iframe去查看最新效果。前面介绍的DevServer支持代理客户端和iframe方法,默认使用代理客户端方法。


在之前的章节我们介绍了如何对文件进行监听,使用了DevServer后会自动启用Webpack的文件监听功能,同时会在项目中注册代理客户端,打开浏览器开发者工具会发现代理客户端向DevServer发起的webscoket链接。

通过webscoket链接,当监听到项目文件(实现原理为获取文件的最后修改时间,如果修改时间和上一次不一致,表示文件做出了修改),便会告诉客户端进行页面的刷新,实现了自动刷新。


自动刷新是刷新整个页面,当我们项目中使用了状态管理的时候,比如使用Redux管理数据的时候,这种方式就会重置Redux中的数据。这个时候使用模块热替换就更符合开发需求(热更新)。


热更新相对于前面的全局刷新,实时预览反应更快,等待时间更短,能保持当前网页的数据不变。之前我们介绍过Webpack构建的项目都是有一个个模块组合起来的,热更新的原理就是一个模块源码发生变化的时候,只需要重新编译发生变化的模块,再用新输出的模块替换浏览器中对应的老模块。


要开启模块热替换,只需在DevServer中进行如下配置:

为了方便验证模块热更新,本章节使用了一个计数器例子,当我们开启模块热更新后,此时我们修改相关模块,发现页面是直接刷新了(计数器的数值进行了重置),并没有达到只更新源码发生变化的模块,需要在入口文件中添加以下代码:

在子模块发生改变的时候,更新事件会一层层地向上传递,直到某层的文件接收了当前的变化,也就是入口文件中的module.hot.accept();就会停止向上传递,不然就会一直向上传递,到最外层都没有文件接受它,那么就会直接刷新页面。在这里入口文件接收了该事件,那么就可以处理模块热替换。


module.hot.accept()接收两个参数,第一个参数是一个数组,表示接收那些子模块的替换,第二个参数表示新的子模块加载完成后要执行的逻辑。现在修改test.js中的源码,会发现计数器的值并没有发生改变(该模块没有被更新),发生变化的是test.js中的内容。


开启模块热替换后,页面会加载额外的js文件和json文件,比如这里的main.cd8cecb1a9ca99c0aa1b.hot-update.js和cd8cecb1a9ca99c0aa1b.hot-update.json 文件,其中对应的Javascript文件是一个补丁文件,里面的内容就是test.js中修改后的源码,通过使用该JavaScript文件中的代码,实现新的模块呈现。目前为止我们都是对JavaScript的热更新做出相应的操作,当我们修改Css的时候,也会触发模块热替换,其原因是当我们使用Style-loader后,该Loader会内部注入用于热更新Css的代码。