Webpack HMR 原理全解析(1)

本文介绍了在HMR出现之前,页面级更新的局限性,然后详细阐述了Webpack5下如何启用和利用HMR进行实时模块更新,包括配置、使用方法、实现原理以及关键接口如module.hot.accept。文中还提到WebpackRuntime和增量构建的过程,展示了HMR如何通过WebSocket实现实时更新,提高开发效率。
摘要由CSDN通过智能技术生成

在 HMR 之前,应用的加载、更新是一种页面级别的原子操作,即使只是单个代码文件发生变更都需要刷新整个页面才能最新代码映射到浏览器上,这会丢失之前在页面执行过的所有交互与状态,例如:

  • 对于复杂表单场景,这意味着你可能需要重新填充非常多字段信息

  • 弹框消失,你必须重新执行交互动作才会重新弹出

再小的改动,例如更新字体大小,改变备注信息都会需要整个页面重新加载执行,影响开发体验。引入 HMR 后,虽然无法覆盖所有场景,但大多数小改动都可以实时热更新到页面上,从而确保连续、顺畅的开发调试体验,对开发效率有较大增益效果。

1.2 使用 HMR


Webpack 生态下,只需要经过简单的配置即可启动 HMR 功能,大致上分两步:

  • 配置 devServer.hot 属性为 true,如:

// webpack.config.js

module.exports = {

// …

devServer: {

// 必须设置 devServer.hot = true,启动 HMR 功能

hot: true

}

};

  • 之后,还需要调用 module.hot.accept 接口,声明如何将模块安全地替换为最新代码,如:

import component from “./component”;

let demoComponent = component();

document.body.appendChild(demoComponent);

// HMR interface

if (module.hot) {

// Capture hot update

module.hot.accept(“./component”, () => {

const nextComponent = component();

// Replace old content with the hot loaded one

document.body.replaceChild(nextComponent, demoComponent);

demoComponent = nextComponent;

});

}

模块代码的替换逻辑可能非常复杂,幸运的是我们通常不太需要对此过多关注,因为业界许多 Webpack Loader 已经提供了针对不同资源的 HMR 功能,例如:

  • style-loader 内置 Css 模块热更

  • vue-loader 内置 Vue 模块热更

  • react-hot-reload 内置 React 模块热更接口

因此,站在使用的角度,只需要针对不同资源配置对应支持 HMR 的 Loader 即可,很容易上手。

二、实现原理

======

Webpack HMR 特性的原理并不复杂,核心流程:

  1. 使用 webpack-dev-server (后面简称 WDS)托管静态资源,同时以 Runtime 方式注入 HMR 客户端代码

  2. 浏览器加载页面后,与 WDS 建立 WebSocket 连接

  3. Webpack 监听到文件变化后,增量构建发生变更的模块,并通过 WebSocket 发送 hash 事件

  4. 浏览器接收到 hash 事件后,请求 manifest 资源文件,确认增量变更范围

  5. 浏览器加载发生变更的增量模块

  6. Webpack 运行时触发变更模块的 module.hot.accept 回调,执行代码变更逻辑

  7. done

接下来我会展开 HMR 的核心源码,详细讲解 Webpack 5 中 Hot Module Replacement 原理的关键部分,内容略微晦涩,不感兴趣的同学可以直接跳到下一章。

2.1 注入 HMR 客户端运行时


执行 npx webpack serve 命令后,WDS 调用 HotModuleReplacementPlugin 插件向应用的主 Chunk 注入一系列 HMR Runtime,包括:

  • 用于建立 WebSocket 连接,处理 hash 等消息的运行时代码

  • 用于加载热更新资源的 RuntimeGlobals.hmrDownloadManifest 与 RuntimeGlobals.hmrDownloadUpdateHandlers 接口

  • 用于处理模块更新策略的 module.hot.accept 接口

  • 等等

关于 Webpack Runti me,可参考  Webpack 原理系列六:彻底理解 Webpack 运行时

经过 HotModuleReplacementPlugin 处理后,构建产物中即包含了所有运行 HMR 所需的客户端运行时与接口。这些 HMR 运行时会在浏览器执行一套基于 WebSocket 消息的时序框架,如图:

2.2 增量构建


除注入客户端代码外,HotModuleReplacementPlugin 插件还会借助 Webpack 的 watch 能力,在代码文件发生变化后执行增量构建,生成:

  • manifest 文件:JSON 格式文件,包含所有发生变更的模块列表,命名为 [hash].hot-update.json

  • 模块变更文件:js 格式,包含编译后的模块代码,命名为 [hash].hot-update.js

增量构建完毕后,Webpack 将触发 compilation.hooks.done 钩子,并传递本次构建的统计信息对象 stats。WDS 则监听 done 钩子,在回调中通过 WebSocket 发送模块更新消息:

{“type”:“hash”,“data”:“${stats.hash}”}

实际效果:

2.3 加载更新


客户端接受到 hash 消息后,首先发出 manifest 请求获取本轮热更新涉及的 chunk,如:

注意,在 Webpack 4 及之前,热更新文件以模块为单位,即所有发生变化的模块都会生成对应的热 更新文件;  Webpack 5 之后热更新文件以 chunk 为单位,如上例中, main  chunk 下任意文件的变化都只会生成  main.[hash].hot-update.js  更新文件。

manifest 请求完成后,客户端 HMR 运行时开始下载发生变化的 chunk 文件,将最新模块代码加载到本地。

2.4module.hot.accept回调


经过上述步骤,浏览器加载完最新模块代码后,HMR 运行时会继续触发 module.hot.accept 回调,将最新代码替换到运行环境中。

module.hot.accept 是 HMR 运行时暴露给用户代码的重要接口之一,它在 Webpack HMR 体系中开了一个口子,让用户能够自定义模块热替换的逻辑。module.hot.accept 接口签名如下:

module.hot.accept(path?: string, callback?: function);

它接受两个参数:

  • path:指定需要拦截变更行为的模块路径

  • callback:模块更新后,将最新模块代码应用到运行环境的函数

例如,对于如下代码:

// src/bar.js

export const bar = ‘bar’

// src/index.js

import { bar } from ‘./bar’;

const node = document.createElement(‘div’)

node.innerText = bar;

document.body.appendChild(node)

module.hot.accept(‘./bar.js’, function () {

node.innerText = bar;

})

示例中,module.hot.accept 函数监听 ./bar.js 模块的变更事件,一旦代码发生变动就触发回调,将 ./bar.js 导出的值应用到页面上,从而实现热更新效果。

module.hot.accept 的作用并不复杂,但使用过程中还是有一些值得注意的点,下面细讲。

最后

推荐一些系统学习的途径和方法。

路线图

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

HTML 和 CSS:

html5知识

css基础知识

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值