一、什么是 HMR
-
HMR 的全称是 Hot Module Replacement ,翻译为模块热替换/模块热更新
-
模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个应用,可以实现只将修改的模块实时替换至应用中
-
默认情况下,
webpack-dev-server
已经支持 HMR,我们只需要开启即可,在不开启 HMR 的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading
const webpack = require('webpack')
module.exports = {
devServer: {
hot: true // 开启 HMR 特性
}
}
二、HMR 提高开发的速度的方式
- 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
- 只更新需要变化的内容,节省开发的时间
- 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的 devtools 中直接修改样式
三、框架的 HMR
-
在开发项目时,是不需要经常手动去写入 module.hot.accpet相关的API的。开发Vue、React项目,我们修改了组件,也会实时进行热更新
-
比如 vue 开发中,使用
vue-loader
,vue-loader 加载的组件默认会帮助我们进行HMR的处理 -
比如 react 开发中,有
React Hot Loader
,实时调整react组件,create-react-app 已经帮忙默认配置好了(目前React官方已经弃用了,改成使用react-refresh
)
四、HMR 的原理
那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?
- webpack-dev-server 会创建两个服务:提供静态资源的服务(express)和 Socket 服务(net.Socket)
express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析),浏览器会发送一个 http 请求,来请求 express 提供的静态资源,http 链接是一个短链接,特点就是 发送请求(客户端) - 建立连接 - 响应结果 - 断开连接
HMR Socket Server,是一个 socket 的长连接: 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端) 当服务器监听到对应的模块发生变化时,会生成两个文件 .json(manifest文件,记录一些修改的信息)和 .js文件(update chunk,记录具体变化的内容) 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器) 浏览器拿到两个新的文件后,通过 HMR runtime 机制,加载这两个文件,并且针对修改的模块进行更新
- 启动阶段为上图 1-2-A-B
首先 Webpack Compile
会将源代码和 HMR Runtime
一起编译成 bundle.js
文件,传输给 Bundle Server
静态资源服务器
- 更新阶段为上图 1-2-3-4
当某一个文件或者模块发生变化时,webpack 监听到文件变化对文件重新编译打包,编译生成唯一的hash
值,这个hash
值用来作为下一次热更新的标识
根据变化的内容生成两个补丁文件:manifest.json
(包含了 hash
和 chundId
,用来说明变化的内容)和chunk.js
模块
由于socket
服务器在HMR Runtime
和 HMR Server
之间建立 websocket
链接,当文件发生改动的时候,服务端会向浏览器推送一条消息,消息包含文件改动后生成的hash
值,如下图的h属性,作为下一次热更新的标识
在浏览器接受到这条消息之前,浏览器已经在上一次socket
消息中已经记住了此时的hash
标识,这时候我们会创建一个 ajax
去服务端请求获取到变化内容的 manifest.json
文件
manifest.json
文件包含重新build
生成的hash
值,以及变化的模块,对应上图的c
属性
浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新