Webpack HMR 原理全解析,2024年最新为什么要学前端开发

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

======

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 的作用并不复杂,但使用过程中还是有一些值得注意的点,下面细讲。

2.4.1 失败兜底

module.hot.accept 函数只接受具体路径的 path 参数,也就是说我们无法通过 glob 或类似风格的方式批量注册热更新回调。

一旦某个模块没有注册对应的 module.hot.accept 函数后,HMR 运行时会执行兜底策略,通常是刷新页面,确保页面上运行的始终是最新的代码。

2.4.2 更新事件冒泡

在 Webpack HMR 框架中,module.hot.accept 函数只能捕获当前模块对应子孙模块的更新事件,例如对于下面的模块依赖树:

示例中,更新事件会沿着模块依赖树自底向上逐级传递,从 foo 到 index ,从 bar-1 到 bar 再到 index,但不支持反向或跨子树传递,也就是说:

  • 在 foo.js 中无法捕获 bar.js 及其子模块的变更事件

  • 在 bar-1.js 中无法捕获 bar.js 的变更事件

这一特性与 DOM 事件规范中的冒泡过程极为相似,使用时如果摸不准模块的依赖关系,建议直接在应用的入口文件中编写热更新函数。

2.4.3 无参数调用

除上述调用方式外,module.hot.accept 函数还支持无参数调用风格,作用是捕获当前文件的变更事件,并从模块第一行开始重新运行该模块的代码,例如:

// src/bar.js

console.log(‘bar’);

module.hot.accept();

示例模块发生变动之后,会从头开始重复执行 console.log 语句。

2.5 小结


回顾整个 HMR 过程,所有的状态流转均由 WebSocket 消息驱动,这部分逻辑由 HMR 运行时控制,开发者几乎无感。

唯一需要开发者关心的是为每一个需要处理热更新的文件注册 module.hot.accept 回调,所幸这部分需求已经被许多成熟的 Loader 处理,作为示例,下一节我们挖掘 vue-loader 源码,学习如何灵活使用 module.hot.accept 函数处理文件更新。

三、 vue-loader 如何实现 HMR

========================

vue-loader 是一个用于处理 Vue Single File Component 的 Webpack 加载器,它能够将如下格式的内容转译为可在浏览器运行的等价代码:

除常规的代码转译外,在 HMR 模式下,vue-loader 还会为每一个 Vue 文件注入一段处理模块替换的逻辑,如:

“./src/a.vue”:

/!*****************!\

!*** ./src/a.vue ***!

*******************/

/***/

((module, webpack_exportswebpack_require) => {

// 模块代码

// …

/* hot reload */

if (true) {

var api = webpack_require( /*! …/node_modules/vue-hot-reload-api/dist/index.js */ “…/node_modules/vue-hot-reload-api/dist/index.js”)

api.install(webpack_require( /*! vue */ “…/node_modules/vue/dist/vue.runtime.esm.js”))

最后更多分享:前端字节跳动真题解析

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
! vue */ “…/node_modules/vue/dist/vue.runtime.esm.js”))

最后更多分享:前端字节跳动真题解析
  • [外链图片转存中…(img-cArFC2ok-1713409729716)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-rOd5l74e-1713409729717)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值