
前端构建构建工具开发工具【免费下载链接】farmExtremely fast Vite-compatible web build tool written in Rust项目地址https://gitcode.com/gh_mirrors/fa/farm点击查看免费下载FarmREADME.md 中定位为 Extremely fast Vite-compatible web build tool written in Rust在 dev 模式下提供开箱即用的模块热更新HMR能力其浏览器侧核心正是farmfe/runtime-plugin-hmr这个运行时插件。本文以该包的 CHANGELOG.md 为演进主线结合 packages/runtime-plugin-hmr 下的完整源码与 packages/core 的注入逻辑讲解 HMR 客户端的工作机制、import.meta.hotAPI 的实现细节、服务端配置项以及每个版本修复的典型问题——读者读完后既能理解 Farm HMR 的底层运转方式也能在自己的业务代码中正确使用import.meta.hot编写可热更新的模块。一、插件定位Farm 的 HMR 客户端以 Runtime Plugin 形态存在farmfe/runtime-plugin-hmr的 package.json 描述为 Runtime hmr plugin of Farm版本号当前为4.0.0-beta.2产物入口为dist/index.js以 ESMtype: module发布运行时依赖core-js与prismjs后者用于错误覆盖层的代码高亮。其 README.md 明确了两条设计准则Farm 的 HMR 客户端与 Vite 兼容API 和实现均参考 Vite实现同时受 Vite 与 esm-hmr 启发。所谓 Runtime Plugin指的是它并不参与编译期处理而是作为一个插件注入到 Farm 运行时模块系统中随产物一起在浏览器端执行。插件本体在 src/index.ts 中只有寥寥数行import { HmrClient } from ./hmr-client.js; import { createHotContext } from ./hot-module-state.js; let hmrClient: HmrClient; export default { name: farm-runtime-hmr-client-plugin, bootstrap(moduleSystem: any) { hmrClient new HmrClient(moduleSystem); hmrClient.connect(); }, moduleCreated(module: any) { // create a hot context for each module module.meta.hot createHotContext(module.id, hmrClient); } };两个钩子各司其职bootstrap模块系统启动时创建HmrClient并建立 WebSocket 连接moduleCreated每个模块创建时为module.meta.hot挂上对应的热上下文Hot Context这正是业务代码中import.meta.hot的来源。源码目录结构清晰对应各职责文件职责src/hmr-client.tsWebSocket 连接、消息分发、热更新应用逻辑src/hot-module-state.tsimport.meta.hot的accept/dispose/prune等 API 实现src/types.tsHMR 消息协议HMRPayload与更新结果类型定义src/overlay.ts浏览器端错误覆盖层ErrorOverlaysrc/logger.ts带[Farm HMR]前缀的分级日志src/utils.tsANSI 转义清理、SWC 错误帧解析等工具函数二、HMR 客户端工作流程从 WebSocket 连接到更新应用2.1 连接建立与编译期常量注入HmrClient的connect()方法hmr-client.ts负责建立连接其中地址的四个关键片段——host、port、path、protocol——均来自编译期注入的全局常量// Inject during compile time const usingClientHost typeof FARM_HMR_HOST boolean; // using client host/port by default const hmrPort usingClientHost ? window.location.port : Number(FARM_HMR_PORT); const hmrHost usingClientHost ? window.location.hostname : FARM_HMR_HOST; const socketProtocol FARM_HMR_PROTOCOL || (location.protocol https: ? wss : ws); const socketHostUrl ${hmrHost}:${hmrPort}${FARM_HMR_PATH};这些常量的类型声明见 src/global.d.ts实际取值由核心包在 dev 模式下写入。具体注入逻辑位于 packages/core/src/config/normalize-config/normalize-runtime.ts当非生产模式、目标环境不是 Node、server.hmr开启且用户未自定义 HMR 插件时核心包会把farmfe/runtime-plugin-hmr的解析路径自动追加进runtime.plugins同时写入defineFARM_HMR_PORT取自server.hmr.port→server.port→ 默认端口 9000、FARM_HMR_HOST、FARM_HMR_PROTOCOL与FARM_HMR_PATH由publicPath与hmr.path拼接。由此可以看出usingClientHost的分支逻辑默认情况下 HMR 客户端直接使用页面自身的 hostname 与端口对应 CHANGELOG 3.5.9 修复的server.hmr.protocol does not work、3.5.10 修复的混合 ws 协议问题见后文只有显式配置了 host/port 时才采用注入值。连接本身使用 WebSocket 子协议farm_hmr便于服务端识别const socket new WebSocket( ${socketProtocol}://${socketHostUrl}, farm_hmr );2.2 消息分发兼容 Vite 的负载类型handleMessagehmr-client.ts根据payload.type分发消息HMRPayload的类型联合定义在 src/types.tstype含义客户端行为farm-updateFarm 原生更新负载触发farm:beforeUpdate/farm:afterUpdate事件进入handleFarmUpdateerror编译错误触发vite:error/farm:error若overlay为真则创建错误覆盖层connected连接成功仅打印调试日志updateVite 兼容的更新消息触发vite:beforeUpdate/vite:afterUpdatejs-update会把更新请求回发到服务端css-update会打印 css link update is not supported yet 警告custom自定义事件转发给customListenersMap中注册的监听器full-reload整页刷新触发vite:beforeFullReload后location.reload()prune模块被移除触发vite:beforePrune/farm:beforePruneclosing服务端优雅关闭调用closeConnectionGracefully()2.3 farm-updateimmutableModules 与 mutableModules 的 eval 执行Farm 与 Vite 的核心差异在于Farm 是编译产物级的热更新更新资源以模块代码字符串的形式下发。服务端在 packages/core/src/server/hmr-engine.ts 中组装负载把变更模块分成immutableModules不可变模块代码与mutableModules可变模块代码两块连同added/changed/removed/boundaries/dynamicResourcesMap一起广播给所有 WebSocket 客户端。客户端侧handleFarmUpdatehmr-client.ts先清除已有错误覆盖层然后执行两块代码字符串// #fix 2262, using eval instead of new Function to avoid the context issue in micro frontend eval(${result.immutableModules}); eval(${result.mutableModules});注释中特别标注了 issue #2262 的修复——在微前端Qiankun场景下new Function存在全局上下文隔离问题因此改用eval执行。这正是 CHANGELOG 4.0.0-beta.2 中 Fix(v2) #2262. HMR does not work with Qiankun framework 的源码落点。2.4 更新应用边界模块boundary与回调触发applyHotUpdateshmr-client.ts按removed→added→changed的顺序处理removed先调用pruneMap中的 prune 回调若模块不在 added 列表中则通过moduleSystem.e(id)清除模块缓存并从注册表删除added通过moduleSystem.a(id)清理已注册模块的缓存changed 但无边界找不到边界模块时直接location.reload()整页刷新对应 CHANGELOG 3.5.5 do not full reload when boundary module not found 的修复方向动态资源通过moduleSystem.sd(dynamicResources, dynamicModuleResourcesMap)注册新增的动态资源边界链对每条boundaries链先清除链上模块缓存再取链尾作为边界模块筛选出selfAcceptCallbacks接受自身与depsAcceptedCallbacks接受被更新依赖两组回调优先触发依赖回调然后执行对应的dispose回调与新导出。值得注意的错误兜底整个边界执行包裹在try/catch中一旦更新失败会记录[Farm HMR] Error occurred while applying hot updates并整页刷新——这对应 CHANGELOG 3.1.4 Reload the page when HMR fails 的行为。2.5 断线重连与优雅恢复socket的close事件处理hmr-client.ts会先触发vite:ws:disconnect/farm:ws:disconnect事件然后进入waitForSuccessfulPing循环以no-cors方式轮询 HTTP 探活请求头Accept: text/x-farm-ping服务端处于中间件模式时返回 426直到服务恢复后location.reload()重新加载页面。CHANGELOG 3.0.3 feat: reconnect hmr server 对应的正是这套断线重连机制。三、import.meta.hot APIHotModuleState 的实现与用法每个模块创建时createHotContexthot-module-state.ts会为模块 id 建立HotModuleState并存入registeredHotModulesMap。有一个容易被忽略的细节热更新发生时若模块已注册会清空其acceptCallbacks再复用状态避免回调被重复累积。3.1 accept三种重载形态accepthot-module-state.ts与 Vite API 对齐支持三种调用方式// 1. self-accept模块接受自身更新无需重载页面 import.meta.hot.accept(() { render(); }); // 2. 接受单个依赖更新 import.meta.hot.accept(./dep.js, (mod) { /* ... */ }); // 3. 接受多个依赖更新 import.meta.hot.accept([./dep1.js, ./dep2.js], ([mod1, mod2]) { /* ... */ });内部实现统一压入acceptCallbacks并记录deps数组供 2.4 节所述的边界筛选使用。非法参数会抛出invalid hot.accept call。仓库中的 examples/hmr 提供了完整可运行示例例如 accept-deps-array.ts 展示了依赖数组形态下的 DOM 局部更新if (import.meta.hot) { import.meta.hot.accept([./accept-deps-data], ([data]) { const div document.getElementById(id); const renderData data.compData(id); div!.innerText renderData; }); }3.2 dispose、prune 与自接受示例dispose(callback)注册清理回调模块被替换前执行回调接收 hot context 的data对象applyHotUpdates中会在触发 accept 回调前先调用对应模块的 disposer。prune(callback)模块被整体移除不再被任何模块引用时触发。decline()目前是空操作acceptExports()打印 acceptExports is not supported for now 调试日志——这两个 API 在 Farm 中处于占位状态。examples/hmr/src/dispose.ts 给出了 self-accept 配合 dispose 的典型写法热更新时用新组件替换 DOM同时通过 dispose 清理旧子节点避免重复挂载if (import.meta.hot) { import.meta.hot.accept(mod { const div document.getElementById(id); div?.appendChild(mod.createChild()); }); import.meta.hot.dispose(() { const div document.getElementById(id); if (div) { while (div.firstChild) { div.removeChild(div.firstChild); } } }); }3.3 invalidate、on/off 与 send与开发服务器的双向通信invalidate(message?)向服务端发送vite:invalidate与farm:invalidate两条自定义消息通知服务端从该模块的父模块开始重新寻找边界——常用于自接受逻辑判断本次更新无法安全处理的场景示例见 invalidate-parent.ts。on(event, cb)/off(event, cb)在customListenersMap中注册/注销自定义事件监听内部用 Map 维护回调数组注销后若列表为空则删除该事件键。send(event, data?)仅在 WebSocket 处于OPEN状态时以{ type: custom, event, data }格式发送消息。四、错误反馈机制ErrorOverlay 与分级日志4.1 浏览器错误覆盖层src/overlay.ts 定义了一个名为farm-error-overlay的自定义元素ErrorOverlay继承HTMLElement通过attachShadow({ mode: open })隔离样式具备以下特征关闭方式点击遮罩层或按Esc键即可关闭模板 footer 中提示 Click outside, press Esc key, or fix the code to dismiss代码高亮使用prismjs对错误帧做语法高亮CHANGELOG 4.0.0-nightly-20241029095811 use prismjs instead of highlightjs improve performance 的落点文件信息栏与终端风格代码块经由renderMessages动态构建ANSI 清理错误信息先经 src/utils.ts 的stripAnsi、cleanStack、splitErrorMessage、extractSwcCodeBlocks等函数清洗其中extractSwcCodeBlocks专门解析 SWC 编译器以╭─[和╰────围栏格式输出的错误帧逐块提取出可高亮展示的代码片段。4.2 日志分级src/logger.ts 提供统一的[Farm HMR]前缀日志debug/log用console.debug/console.logwarn用console.warnerror实际也走console.warn输出便于在开发面板中过滤。CHANGELOG 3.4.1 use log level with debug 即指日志体系向debug级别收敛。五、服务端配置server.hmr 的完整参数说明HMR 客户端所有行为都可由服务端配置驱动。HmrOptions接口定义在 packages/core/src/config/types.ts默认值见 packages/core/src/config/constants.ts配置项默认值说明protocolHMR WebSocket 协议为空时客户端根据页面协议自动选择ws/wss源码见 hmr-client.tshostlocalhostHMR 服务 host客户端默认使用页面 hostname对应usingClientHost分支portundefinedHMR 端口可经环境变量FARM_DEFAULT_HMR_PORT指定clientPort9000HMR 客户端端口path/__hmrHMR 端点路径timeout0HMR 连接超时毫秒overlaytrue是否在浏览器显示错误覆盖层对应 CHANGELOG 3.5.3 Support disable overlayserverundefined要挂载 HMR 中间件的自定义 server 实例在farm.config.ts中可按需覆盖import { defineConfig } from farmfe/core; export default defineConfig({ server: { port: 9000, hmr: { host: localhost, path: /__hmr, protocol: ws, overlay: true, timeout: 0 } } });两个值得注意的联动细节自动注入normalizeRuntimeConfig只有在server.hmr非空且目标环境非 Node 时才注入 HMR 客户端插件与FARM_HMR_*define见 normalize-runtime.ts端口同步packages/core/src/server/index.ts 的syncServerPort会在server.hmr.port server.port即未单独指定 HMR 端口时把实际监听端口同步回hmr.port与FARM_HMR_PORT保证服务端口被占用后自动换端口时 HMR 依然连通。六、CHANGELOG 版本演进按主题梳理的能力迭代farmfe/runtime-plugin-hmr的版本历史恰好勾勒出 Farm HMR 能力的成长曲线。下面按主题把 CHANGELOG.md 中的条目归类解读6.1 兼容性与生态v4 时代版本变更对应源码/说明4.0.0-beta.2修复 Qiankun 微前端下 HMR 失效#2262handleFarmUpdate改用eval执行模块代码hmr-client.ts4.0.0-beta.1修复 Windows 上存在语法错误时 HMR panic客户端侧错误负载容错4.0.0-beta.0随 Farm v2.0.0 发布Major与farmfe/runtime大版本联动4.0.0-nightly-20250410022118优化 runtime-hmr 体积产物层面裁剪4.0.0-nightly-20241029095811用prismjs替换highlightjs提升性能overlay.ts 的highlightCode依赖window.Prism6.2 连接与协议稳定性v3.5 系列3.5.10修复将在线 host 代理到 localhost 时混合 ws 协议wss与ws混用的问题3.5.9修复server.hmr.protocol不生效——即 5.1 节FARM_HMR_PROTOCOL注入链路3.5.8bundle 运行时包减少产物体积与加载开销3.5.7修复 #1755并支持 plugin-react 的useAbsolutePath3.5.3支持禁用 overlayserver.hmr.overlay: false同时自动为缺失的 socket URL 补/对应FARM_HMR_PATH拼接逻辑3.5.5/3.5.4多页面应用MPA场景下找不到边界模块时避免误整页刷新、修复多页应用 HMR 报错——对应applyHotUpdates中边界缺失时的continue分支hmr-client.ts。6.3 构建性能与缓存联动v3.2–3.43.3.0支持持久化缓存与增量构建重构 render pot 渲染并优化 sourcemap 生成同时修复import.meta.url运行时问题3.3.1修复 #769 并优化缓存3.2.5用window代替import.meta规避部分运行时环境兼容问题3.2.4修复 HMR runtime 缺少data属性3.2.2修复 Vue SFC 在 HMR 时未移除旧 CSS 样式的问题3.2.1移除 HMR 的 HTTP 请求改用 WebSocket eval——这是架构级变更服务端只通过 ws 推送farm-update客户端eval执行模块代码3.2.0支持通过配置注入 HMR 端口与 host即FARM_HMR_PORT/FARM_HMR_HOST注入机制。6.4 早期能力奠基v3.0–v1.03.0.3reconnect hmr server断线重连3.0.2资源写入磁盘以优化加载时间3.1.2修复懒编译模块 HMR 不更新并支持 Vue SFC sourcemap3.1.1修复 HMR 不移除 CSS 样式3.0.5限制监听文件数以优化冷启动并修复懒编译问题1.0.0Support react HMR——HMR 客户端的起点。从这条时间线可以清晰看到 Farm HMR 的演进脉络先解决 React 框架的 HMRv1.0再通过 WebSocket eval 重构通信协议v3.2.1随后围绕多页应用、懒编译、Vue SFC、CSS 清理等边界场景持续修补v3.1–v3.5最终在 v4 时代修复微前端兼容性问题并大幅裁剪体积。七、实战验证用 examples/hmr 跑通全链路仓库中的 examples/hmr 是 HMR 客户端能力的活文档覆盖了 self-accept、依赖数组 accept、字符串 accept、dispose、invalidate 等全部典型场景。其 package.json 提供了标准的 Farm 命令# 启动开发服务器启用 HMR pnpm start # 等价于 farm start # 生产构建 pnpm build # 等价于 farm build # 预览生产构建产物 pnpm preview # 等价于 farm preview启动后在浏览器修改 src/data.ts 或各组件文件即可观察到有import.meta.hot.accept()的模块如 index.ts无刷新热替换依赖变化时accept 回调中的 DOM 局部更新accept-deps-array.ts修改无热边界覆盖的模块时触发整页 reload故意制造语法错误时ErrorOverlay以代码高亮形式展示 SWC 错误帧。调试时可在浏览器控制台看到[Farm HMR]前缀的 debug/warn 日志配合 Network 面板的ws://localhost:9000/__hmr连接与 WebSocket 消息内容即可完整观察farm-update负载中的immutableModules/mutableModules与boundaries字段印证 2.3–2.4 节的执行路径。结语farmfe/runtime-plugin-hmr虽然只是一个运行时插件却承载了 Farm 开发体验的核心WebSocket 连接管理、Vite 兼容的消息协议、边界模块计算与回调触发、错误覆盖层与断线重连。其版本演进CHANGELOG.md忠实记录了从 React HMR 起步到 WebSocket eval 重构再到 Qiankun 微前端兼容与体积优化的全过程。理解这份源码与配置既能帮助你排查 HMR 失效类问题协议、端口、overlay、MPA 边界也能让你在业务代码中写出与 Vite 生态无缝迁移的import.meta.hot逻辑。赞分享前端构建构建工具开发工具【免费下载链接】farmExtremely fast Vite-compatible web build tool written in Rust项目地址https://gitcode.com/gh_mirrors/fa/farm点击查看免费下载相关推荐免费离线流程图编辑器draw.io Desktop 完整本地化指南免费离线流程图编辑器draw.io Desktop 完整本地化指南 draw.io Desktop 是基于 Electron 的桌面画图编辑器把官方 dra前端构建构建工具开发工具Fresh 2 的 Vite 集成实战配置 fresh/plugin-vite、HMR 与从 Builder 迁移Fresh 2 的 Vite 集成实战配置 fresh/plugin vite、HMR 与从 Builder 迁移 Fresh 2 使用 Vite http后端前端NativeScript 的 Vite 集成演进全解析从 1.0 到 8.x 的 HMR 引擎与框架 Flavor 架构NativeScript 的 Vite 集成演进全解析从 1.0 到 8.x 的 HMR 引擎与框架 Flavor 架构 导读 nativescript/v上一篇Refit.Testing 实战指南用路由表驱动 Refit 客户端覆盖路由匹配、响应桩、重试与超时下一篇AlgoNote 算法通关手册0358. K 距离间隔重排字符串——贪心策略与优先队列最大堆实战解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考