Webpack 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 的 Webp 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 ack 加载器,它能够将如下格式的内容转译为可在浏览器运行的等价代码:
除常规的代码转译外,在 HMR 模式下,vue-loader
还会为每一个 Vue 文件注入一段处理模块替换的逻辑,如:
“./src/a.vue”:
/!*****************!\
!*** ./src/a.vue ***!
*******************/
/***/
((module, webpack_exports, webpack_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”))
if (api.compatible) {
module.hot.accept()
if (!api.isRecorded(‘45c6ab58’)) {
api.createRecord(‘45c6ab58’, component.options)
} else {
api.reload(‘45c6ab58’, component.options)
}
module.hot.accept( /*! ./a.vue?vue&type=template&id=45c6ab58& */ “./src/a.vue?vue&type=template&id=45c6ab58&”, WEBPACK_OUTDATED_DEPENDENCIES => {
/* harmony import */
a_vue_vue_type_template_id_45c6ab58___WEBPACK_IMPORTED_MODULE_0_ = webpack_require( /*! ./a.vue?vue&type=template&id=45c6ab58& */ “./src/a.vue?vue&type=template&id=45c6ab58&”);
(function () {
api.rerender(‘45c6ab58’, {
render: a_vue_vue_type_template_id_45c6ab58___WEBPACK_IMPORTED_MODULE_0_.render,
staticRenderFns: a_vue_vue_type_template_id_45c6ab58___WEBPACK_IMPORTED_MODULE_0_.staticRenderFns
})
})(WEBPACK_OUTDATED_DEPENDENCIES);
})
}
}
// …
/***/
}),
Webpack HMR 原理全解析相关推荐
- Webpack 热更新HMR 原理全解析
这是 Webpack 原理分析系列第十篇文章,前文可到公众号[Tecvan]查阅. 一.什么是 HMR HMR 全称 Hot Module Replacement,中文语境通常翻译为模块热更新,它能够 ...
- Webpack HMR 原理解析
Hot Module Replacement(以下简称 HMR)是 webpack 发展至今引入的最令人兴奋的特性之一 ,当你对代码进行修改并保存后,webpack 将对代码重新打包,并将新的模块发送 ...
- bobsmith电路阻抗原理_串联谐振原理全解析 - 赫兹电力
串联谐振赫兹电力为您导读:串联谐振原理全解析,串联谐振交流耐压试验在发电机绝缘试验中占据至关重要的地位,今天我们就来系统学习一下如何谐振及其原理解析吧. 谐振基础知识 谐振电路是在具有电阻R.电感L. ...
- Leon : YoloV5 结构原理全解析 思维导图版
Leon : YoloV5 结构原理全解析 思维导图版 博客写到了 Head 部分 暂时断更 几天 --因为 互联网加的项目 快提交了 队员们 陆续 完成了 自己负责的策划书部分 得 开始 去修改策划 ...
- STM32 IAP 在线升级原理全解析
点击左上角的"关注",定期更新 STM32 最新资讯,总有你想要的信息! STM32 IAP 在线升级原理全解析 1. 什么是 IAP? IAP(In-Application ...
- hmr webpack 不编译_一文搞懂 webpack HMR 原理
关注「前端向后」微信公众号,你将收获一系列「用心原创」的高质量技术文章,主题包括但不限于前端.Node.js以及服务端技术 一.HMR Hot Module Replacement(HMR)特性最早由 ...
- PFC工作原理全解析
前言 概述 原理分析 BOOST拓扑分析 BOOST电路双闭环控制 PFC工作原理与控制逻辑 大家好,我是大昌,今天给大家分析PFC的工作原理,妥妥的干货,马上开始. 概述 传统应用中输入侧交流电源经 ...
- 淘宝小部件 Canvas 渲染流程与原理全解析
作者:史健平(楚奕) 上篇回顾:<淘宝小部件:全新的开放卡片技术!>.<淘宝小部件在 2021 双十一中的规模化应用> 本文主要从技术视角阐述 Canvas 在小部件下的渲染原 ...
- HashMap底层原理全解析
作为面试中的高频题目,我相信每一个java程序员都有必要搞懂HashMap的底层原理和实现细节,废话不多说直接开撸. 首先简单说一下HashMap的实现原理: 首先有一个Node<k,v> ...
最新文章
- 程序员吐槽:在阿里工作带来光环,在京东却带来负面影响!
- LeetCode 98. Validate Binary Search Tree--C++解法--判断是否是BST--递归,迭代做法,中序遍历
- mysql原生查询单条数据_原生查询数据库流程
- 深入理解AbstractQueuedSynchronizer(AQS)
- 使命召唤手游迎来欧阳娜娜,这阵容够豪华,玩家期待吗?
- MFC中如何给对话框添加背景图片
- Android studio断点调试(全在这里)
- 常用的分布式唯一ID生成方案
- 《R语言机器学习:实用案例分析》——1.2节R的数据结构
- web 前端 如何分享到instagram_面对前端的后端化趋势,2020该如何学习web前端?
- gis属性表怎么导成excel_将Excel数据导入到ArcGIS属性表
- 61. Catalog 分类页面商品排序
- ajax 图片加载不出来,jQuery.lazy()插件不能处理通过AJAX加载的图像
- RTL8153 VC CG
- 隧道保活超时或协商超时_丰巢快递柜超时收费的法律分析
- Photoshop使用技巧
- 创业公司如何搭建服务器配置方案?
- ROS-talker,listener
- python -不敢表白,不好意思说出来,没关系,7行代码完成自动打印文字
- Qt添加MQTT模块