webpack 模块热替换

116 阅读3分钟

一、什么是 HMR

  • HMR 的全称是 Hot Module Replacement ,翻译为模块热替换/模块热更新

  • 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个应用,可以实现只将修改的模块实时替换至应用中

  • 默认情况下,webpack-dev-server 已经支持 HMR,我们只需要开启即可,在不开启 HMR 的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading

const webpack = require('webpack')
module.exports = {
  devServer: {
    hot: true // 开启 HMR 特性
  }
}

二、HMR 提高开发的速度的方式

  1. 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
  2. 只更新需要变化的内容,节省开发的时间
  3. 修改了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 机制,加载这两个文件,并且针对修改的模块进行更新

image.png

  • 启动阶段为上图 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属性,作为下一次热更新的标识

image.png

在浏览器接受到这条消息之前,浏览器已经在上一次socket 消息中已经记住了此时的hash 标识,这时候我们会创建一个 ajax 去服务端请求获取到变化内容的 manifest.json 文件

manifest.json文件包含重新build生成的hash值,以及变化的模块,对应上图的c属性

浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新

image.png