hmr webpack 不编译_一文搞懂 webpack HMR 原理

article/2025/10/7 13:10:25

854bd805f3ab3e1551a54b10dc93cf26.png

关注「前端向后」微信公众号,你将收获一系列「用心原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术

一.HMR

Hot Module Replacement(HMR)特性最早由 webpack 提供,能够对运行时的 JavaScript 模块进行热更新(无需重刷,即可替换、新增、删除模块):

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload.

(摘自Hot Module Replacement Concepts)

与整个重刷相比,模块级热更新最大的意义在于能够保留应用程序的当前运行时状态,让更加高效的Hot Reloading开发模式成为了可能

P.S.后来其它构建工具也实现了类似的机制,例如Browserify、甚至React Native Packager

可是,编辑源码产生的文件变化在编译时,替换模块实现在运行时,二者是怎样联系起来的呢?

二.基本原理

8de98e4a84074fe2859bd6a5570cdc8f.png

监听到文件变化后,通知构建工具(HMR plugin),将发生变化的文件(模块)发送给跑在应用程序里的运行时框架(HMR Runtime),由运行时框架把这些模块塞进模块系统(新增/删除,或替掉现有模块)

其中,HMR Runtime 是构建工具在编译时注入的,通过统一的模块 ID 将编译时的文件与运行时的模块对应起来,并暴露出一系列 API 供应用层框架(如 React、Vue 等)对接

三.HMR API

最常用的是accept:

module.hot.accept(dependencies, callback):监听指定依赖模块的更新

例如:

import printMe from './print.js';

if (module.hot) {

module.hot.accept('./print.js', function() {

console.log('Accepting the updated printMe module!');

printMe();

})

}

触发accept(回调)时,表示新模块已经塞进模块系统了,在此之后访问到的都是新模块实例

P.S.完整示例,见Hot Module Replacement Guides

然而,实际场景中模块间一般存在多级依赖,替换一个模块会影响(直接或间接)依赖到它的所有模块:

41dd6774e85577d023b4b9089cf58517.png

那岂不是要在所有模块中都添一段类似的更新处理逻辑?

通常不需要,因为模块更新事件有冒泡机制,未经accept处理的更新事件会沿依赖链反向传递,只需要在一些重要的节点(比如Router组件)上集中处理即可

除accept外,还提供了:

module.hot.decline(dependencies):将依赖项标记为不可更新(期望整个重刷)

module.hot.dispose/addDisposeHandler(data => {}):当前模块被替换时触发,用来清理资源或(通过data参数)传递状态给新模块

module.hot.invalidate():让当前模块失效,用来强制更新当前模块

module.hot.removeDisposeHandler(callback):取消监听模块替换事件

P.S.关于 webpack HMR API 的具体信息,见Hot Module Replacement API

四.HMR Runtime

从应用程序的角度来看,模块替换过程如下:

应用程序要求 HMR Runtime 检查更新

HMR Runtime 异步下载更新并通知应用程序

应用程序要求 HMR Runtime 应用这些更新

HMR Runtime 同步应用更新

接到(构建工具发来的)模块更新通知后,HMR Runtime 向 Webpack Dev Server 查询更新清单(manifest),接着下载每一个更新模块,所有新模块下载完成后,准备就绪,进入应用阶段

将更新清单中的所有模块都标记为失效,对于每一个被标记为失效的模块,如果在当前模块没有发现accept事件处理,就向上冒泡,将其父模块也标记失效,一直冒到应用入口模块

之后所有失效模块被释放(dispose),并从模块系统中卸载掉,最后更新模块 hash 并调用所有相关accept事件处理函数

五.实现细节

实现上,应用程序在初始化时会与 Webpack Dev Server 建立 WebSocket 连接:

dab68a94104dddb1162589de77f5a8ba.png

Webpack Dev Server 向应用程序发出一系列消息:

o

a["{"type":"log-level","data":"info"}"]

a["{\"type\":\"hot\"}"]

a["{"type":"liveReload"}"]

a["{"type":"hash","data":"411ae3e5f4bab84432bf"}"]

a["{"type":"ok"}"]

文件内容发生变化时,Webpack Dev Server 会通知应用程序:

a["{"type":"invalid"}"]

a["{"type":"invalid"}"]

a["{"type":"hash","data":"a0b08ce32f8682379721"}"]

a["{"type":"ok"}"]

接着,HMR Runtime 发起 HTTP 请求获取模块更新清单:

XHR GET http://localhost:8080/411ae3e5f4bab84432bf.hot-update.json

{"h":"a0b08ce32f8682379721","c":{"main":true}}

通过script标签“下载”所有模块更新:

SCRIPT SRC http://localhost:8080/main.411ae3e5f4bab84432bf.hot-update.js

webpackHotUpdate("main", {

"./src/App.js": (function(module, __webpack_exports__, __webpack_require__) {

// (新的)文件内容

})

})

如此这般,运行时的 HMR Runtime 顺利拿到了编译时的文件变化,接下来将新模块塞进模块系统(modules大表):

// insert new code

for (moduleId in appliedUpdate) {

if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {

modules[moduleId] = appliedUpdate[moduleId];

}

}

最后通过accept事件通知应用层使用新的模块进行“局部刷新”:

// call accept handlers

for (moduleId in outdatedDependencies) {

module = installedModules[moduleId];

if (module) {

moduleOutdatedDependencies = outdatedDependencies[moduleId];

var callbacks = [];

for (i = 0; i < moduleOutdatedDependencies.length; i++) {

dependency = moduleOutdatedDependencies[i];

cb = module.hot._acceptedDependencies[dependency];

if (cb) {

if (callbacks.indexOf(cb) !== -1) continue;

callbacks.push(cb);

}

}

for (i = 0; i < callbacks.length; i++) {

// 触发accept模块更新事件

cb(moduleOutdatedDependencies);

}

}

}

至此,水落石出

参考资料

What exactly is Hot Module Replacement in Webpack?

Understanding webpack HMR beyond the docs

Introducing Hot Reloading

联系我

如果心中仍有疑问,请查看原文并留下评论噢。(特别要紧的问题,可以直接微信联系 ayqywx )


http://chatgpt.dhexx.cn/article/YDFl1lhJ.shtml

相关文章

Webpack HMR 原理全解析

执行 npx webpack serve 命令后&#xff0c;WDS 调用 HotModuleReplacementPlugin 插件向应用的主 Chunk 注入一系列 HMR Runtime&#xff0c;包括&#xff1a; 用于建立 WebSocket 连接&#xff0c;处理 hash 等消息的运行时代码 用于加载热更新资源的 RuntimeGlobals.hmrDow…

vite1.x 热更新(HMR)的实现原理

前言 将近一年前自己尝试阅读vite源码&#xff08;2.x&#xff09;&#xff0c;虽然也有些收获但整体并没有到达我的预期&#xff0c;对于vite也是停留在一知半解的程度上。最近想重新开始学习vite&#xff0c;但回顾之前的学习历程&#xff0c;感觉不太想继续之前的方式&…

Webpack HMR 原理解析

Hot Module Replacement&#xff08;以下简称 HMR&#xff09;是 webpack 发展至今引入的最令人兴奋的特性之一 &#xff0c;当你对代码进行修改并保存后&#xff0c;webpack 将对代码重新打包&#xff0c;并将新的模块发送到浏览器端&#xff0c;浏览器通过新的模块替换老的模…

Webpack的HMR原理解析

Hot Module Replacement&#xff08;以下简称 HMR&#xff09;是 webpack 发展至今引入的最令人兴奋的特性之一 &#xff0c;当你对代码进行修改并保存后&#xff0c;webpack 将对代码重新打包&#xff0c;并将新的模块发送到浏览器端&#xff0c;浏览器通过新的模块替换老的模…

Esbuild Bundler HMR

Esbuild 虽然 bundler 非常快&#xff0c;但是其没有提供 HMR 的能力&#xff0c;在开发过程中只能采用 live-reload 的方案&#xff0c;一有代码改动&#xff0c;页面就需要全量 reload &#xff0c;这极大降低开发体验。为此添加 HMR 功能至关重要。 经过调研&#xff0c;社…

Vite HMR

传统webpack的hmr是使用webpack的HotModuleReplacementPlugin&#xff0c;而vite则是采用native ES Module的devServer。 初始化本地服务器加载并运行对应的plugin 最重要的一件事就是运行plugin&#xff0c;目前vite支持的plugin大体如下图所示 1、建立ViteDevServer服务器…

webpack HMR

HMR或者hot模式下&#xff0c;启动webpack会在浏览器与服务器之间会建立一个websocket连接&#xff0c;使得浏览器可以和服务端建立全双工通信&#xff1b;当应用程序的代码更新时&#xff0c;会要求HMR runtime检查更新&#xff0c;有更新时&#xff0c;在websoket连接中会返回…

webpack中的HMR(热更新)原理剖析

简介 Hot Module Replacement&#xff08;以下简称 HMR&#xff09;是 webpack 发展至今引入的最令人兴奋的特性之一 &#xff0c;当你对代码进行修改并保存后&#xff0c;webpack 将对代码重新打包&#xff0c;并将新的模块发送到浏览器端&#xff0c;浏览器通过新的模块替换…

HMR(热替换)

HMR 即模块热替换&#xff08;hot module replacement&#xff09;的简称&#xff0c;它可以在应用运行的时候&#xff0c;不需要刷新页面&#xff0c;就可以直接替换、增删模块。webpack 可以通过配置 webpack.HotModuleReplacementPlugin 插件来开启全局的 HMR 能力&#xff…

面试官:说说Webpack的热更新是如何做到的?原理是什么?

一、是什么 HMR全称 Hot Module Replacement&#xff0c;可以理解为模块热替换&#xff0c;指在应用程序运行过程中&#xff0c;替换、添加、删除模块&#xff0c;而无需重新刷新整个应用 例如&#xff0c;我们在应用运行过程中修改了某个模块&#xff0c;通过自动刷新会导致整…

Webpack 热更新HMR 原理全解析

一、什么是 HMR HMR 全称 Hot Module Replacement&#xff0c;中文语境通常翻译为模块热更新&#xff0c;它能够在保持页面状态的情况下动态替换资源模块&#xff0c;提供丝滑顺畅的 Web 页面开发体验。 HMR 最初由 Webpack 设计实现&#xff0c;至今已几乎成为现代工程化工具…

curl.perform() pycurl.error: (23, 'Failed writing body (0 != 59)')

在使用python3.7编码时&#xff0c;引入pycurl模块和StringIO模块后&#xff0c;容易引起上述错误 导入StringIO模块的解决方案&#xff1a; 只有在python2中才能导入StringIO模块&#xff0c;直接 from StringIO import StringIO 即可 但是python3&#xff0c;STringIO和…

关于python的url处理

基本环境&#xff1a; python2.7 1 完整的url语法格式&#xff1a; 协议://用户名密码:子域名.域名.顶级域名:端口号/目录/文件名.文件后缀?参数值#标识 2 urlparse模块对url的处理方法 urlparse模块对url的主要处理方法有&#xff1a;urljoin/urlsplit/urlunsplit/urlpar…

windows10+python3.7使用pip安装pycurl失败

使用pip install pycurl安装pycurl失败&#xff1a; python setup.py egg_info did not run successfully. 可以单独下载pycurl依赖文件然后安装 sArchived: Python Extension Packages for Windows - Christoph Gohlke (uci.edu) 选择Python对应版本的文件进行下载&#xff0…

Pycurl介绍

pycurl — A Python interface to the cURL library Pycurl包是一个libcurl的Python接口.pycurl已经成功的在Python2.2到Python2.5版编译测试过了. Libcurl是一个支持FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 和 LDAP的客户端URL传输库.libcurl也支持HTTPS认证,H…

[windows]python 安装pycurl

问题描述 pip install pycurl 报错 手动安装 下载地址&#xff1a;https://www.lfd.uci.edu/~gohlke/pythonlibs/ 页面搜索&#xff1a; pycurl 下载对应版本的whl文件&#xff0c;我是windows环境 python3.8 所以下载pycurl-7.45.1-cp38-cp38-win32.whl 安装&#xff1a;…

Python实用模块之pycurl

软硬件环境 ubuntu 19.04 64bitanaconda3 with python 3.7.3pycurl 7.43.0.2 简介 CURL是一个基于URL进行数据传输的命令行工具&#xff0c;使用C语言编写&#xff0c;支持http&#xff0c;https&#xff0c;ftp&#xff0c;telnet&#xff0c;file&#xff0c;ldap等常见网络传…

ipcs -a

消息队列、共享内存、信号量

ipcc

IPCCX装完后连接不了LDAP,怎么解决&#xff1f;&#xff1f; 装完了IPCCX, 靠&#xff0c; 直接给我来歌60秒关机&#xff0c; 后面还有LDAP连接问题&#xff0c; 我的IPCCX server 可以ping通ccm server, 为什么LDAP会挂呢&#xff1f;&#xff1f;&#xff1f; 请问我现在要怎…

ipcs报错:kernel not configured for shared memory、semaphore、message queues [解决方法]

前言 今天在复习linux进程间通信的shared memory 共享内存时&#xff0c;在PC端的VMare Workstation虚拟机的Ubuntu上测试我写的shared_memory_CREAT.c 和shared_memory_CONSUME.c 时正常在PC端运行&#xff0c;就想着把程序用交叉编译器编译成arm格式放到linux开发板上运行试…