构建工具移动开发CLI【免费下载链接】metro The JavaScript bundler for React Native项目地址https://gitcode.com/gh_mirrors/me/metro点击查看免费下载MetroReact Native 的 JavaScript 打包器通过将模块转换transformation结果缓存起来避免了源码或配置未变化时的重复计算从而显著加速构建。本文以官方文档 docs/Caching.md 为核心结合metro-cache、metro-config与metro三个包的真实源码系统讲解 Metro 的缓存工作原理、cacheStores配置方法、内置缓存存储的用法与参数以及如何实现自定义缓存存储帮助你在单机与团队场景下正确配置和调优缓存。一、缓存体系总览本地缓存与远程缓存Metro 在开箱状态下即使用本地缓存来存储已转换模块的结果关于转换阶段的概念可参考 docs/Concepts.md。得益于该缓存Metro 不需要重新转换每个模块——除非自上次转换以来该模块的源码或当前配置发生了变化。在本地缓存之外Metro 还支持远程缓存。对于大型团队和/或大型代码库远程缓存可以进一步减少本机构建远端变更所花费的时间从而大幅加速构建。官方文档提到这正是 Meta 内部使用 Metro 构建 React Native 应用的方式一个拥有成千上万个文件、数百名日常活跃工程师的代码库。典型的远程缓存部署方案一个标准的远程缓存配置通常包含三个环节准备一个团队专用的存储后端例如 S3 bucket。周期性运行metro build命令例如放在 CI 任务中来填充缓存在 Metro 配置中使用HttpStore或自定义的读写缓存存储。在开发机上配置 Metro 从缓存读取在 Metro 配置中使用HttpGetStore或自定义的只读缓存存储。metro build是 CLI 提供的构建命令其常用选项如下详见 docs/CLI.md选项别名说明取值outO输出 bundle 的文件名Stringplatformp打包的目标平台web、android、iosminifyz是否压缩 bundleBooleandevg是否生成开发版构建process.env.NODE_ENV developmentBooleanconfigc使用的metro.config.js路径Stringmax-workersj并行转换的 worker 数Numberproject-rootsP项目根目录Arraysource-map—是否生成 source mapBooleansource-map-url—source map 的地址Stringresolver-option—自定义 resolver 选项形式keyvalueArraytransform-option—自定义 transform 选项形式keyvalueArray也就是说CI 中一条类似metro build src/index.js --out dist/bundle.js --platform android的命令即可产出 bundle并在此过程中通过HttpStore把每个模块的转换结果写入远程缓存开发机上则通过HttpGetStore命中这些结果无需在本地重新转换。二、cacheStores配置本地优先、远程兜底Metro 缓存的核心配置项是cacheStores。官方文档明确建议本地缓存如FileStore应排在列表首位远程缓存如HttpStore紧随其后。这样排列的原因是 Metro 缓存读取的语义当 Metro 需要转换一个模块时它会先为该文件计算一个机器无关的缓存键然后按顺序尝试从每个 store 中读取。一旦拿到转换结果无论来自缓存还是重新计算它会将该结果写入所有对该键返回了null缓存未命中的 store。这一行为在 Cache.js 中体现得非常清楚Cache.get()顺序遍历#stores数组首次命中即返回Cache.set()则把值写入从第一个 store 起、直到命中该键的那个 store 为止的所有 store通过#hits这个WeakMap记录“哪个 store 命中了这个键”避免把已存在的值重复写回。此外Cache类为每次get/set记录日志日志项包含 store 名与十六进制键并通过action_result: hit | miss标记缓存命中与未命中。配置cacheStores有两种形式直接传入 store 实例数组或传入以metro-cache包导出对象为参数的函数。文档给出的函数形式示例如下// metro.config.js const os require(node:os); const path require(node:path); module.exports { cacheStores: ({ FileStore }) [ new FileStore({ root: path.join(os.tmpdir(), metro-cache), }), ], };注意FileStore、AutoCleanFileStore、HttpStore、HttpGetStore等类既可以从metro-cache包直接导入也可以通过cacheStores的函数形式MetroCache参数拿到后者避免了在配置文件中额外引入包依赖。配置解析层会在调用该函数时自动注入metro-cache的导出见 loadConfig.js 与测试 loadConfig-test.js。cachestores.config.js 是仓库中一个真实的函数形式配置示例module.exports { cacheStores: ({ FileStore }) { return [new FileStore({ root: __dirname })]; }, };如果不配置该选项Metro 的默认行为是仅使用一个临时目录下的FileStore。默认配置位于 defaults/index.jscacheStores: [ new FileStore({ root: path.join(os.tmpdir(), metro-cache), }), ],相关配置项还有两个值得关注cacheVersion默认1.0参与全局缓存键的计算。当你调整了会改变转换结果的配置时应提升该版本号使旧缓存失效详见 docs/Configuration.md。resetCache设为true时Metro 会在启动时重置转换缓存与文件映射缓存见 docs/Configuration.md。三、内置缓存存储逐个解析Metro 为cacheStores提供了若干内置实现源码位于 packages/metro-cache/src/stores统一从 packages/metro-cache/src/index.js 导出。FileStore({root})将缓存条目作为文件存放到root指定的目录下。从 FileStore.js 可以看出其磁盘布局#getFilePath(key: Buffer): string { return path.join( this.#root, key.slice(0, 1).toString(hex), // 键的第一个字节作为子目录 key.slice(1).toString(hex), // 剩余字节作为文件名 ); }即每个缓存键对应root/首字节 hex/其余字节 hex路径下的一个文件文件内容为 JSON 序列化的值如果值是Buffer则在前面加一个0x00字节作为类型标记读取时据此还原见 FileStore.js。写入时会自动递归创建目录读取时遇到ENOENT或 JSON 解析错误一律视为未命中返回nullclear()则会删除root下的全部 256 个子目录。AutoCleanFileStore()已弃用一个会定期清理旧条目的FileStore接受与FileStore相同的root参数另加两个选项intervalMs: number两次清理尝试之间的时间间隔毫秒默认10 分钟cleanupThresholdMs: number条目距上次修改至少多久才允许被删除毫秒默认3 天。对应源码见 AutoCleanFileStore.js默认值分别为10 * 60 * 1000与3 * 24 * 60 * 60 * 1000。文档与源码注释都明确标注该实现已弃用其实现不够高效缓存较大时可能产生大量冗余 I/O。官方建议改用你自己的清理脚本或使用基于监听watches、挂钩 get/set、实现 LRU 的自定义缓存存储。HttpStore(options)一个轻量级bare-bones的远程缓存客户端通过 HTTP/HTTPS 执行读取GET与写入PUT传输的是压缩后的缓存产物。其核心选项如下endpoint: string缓存服务器的基础 URL。文档给出的示例是HttpStore配http://www.example.com/endpoint时发出的请求形如http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d——即键的十六进制字符串直接拼在endpoint路径之后见 HttpStore.js 中path: ${path}/${key.toString(hex)}。timeout: number请求超时时间毫秒默认5000。family: 4 | 6与 Node.jshttp.request的family参数含义一致。cert、ca、keyHTTPS 选项直接透传给 Node.js 内置的 HTTPS 客户端。从源码看HttpStore还支持更多未在文档中展开的选项值得了解getOptions/setOptions可以为读、写分别指定不同的端点配置HttpStore.js例如读走只读端点、写走可写端点params: URLSearchParams、headers附加在请求 URL 与请求头上的额外参数additionalSuccessStatuses额外视为成功的 HTTP 状态码默认仅 2xx 与 404 特例maxAttempts、retryNetworkErrors、retryStatuses重试配置。重试基于exponential-backoff实现采用 full jitter最大延迟 30 秒仅在错误属于HttpError且状态码命中retryStatuses、或属于NetworkError且开启retryNetworkErrors时才重试HttpStore.jsproxy通过https-proxy-agent支持代理debug出错时在错误信息中附加响应体便于排查。在协议层面GET返回 404 视为未命中返回null返回 200 时响应体会被 gzip 解压再按“0x00前缀的 Buffer 或 JSON”规则还原PUT写入时用 gzip压缩级别 9压缩请求体。相关的HttpError带 HTTP 状态码与NetworkError分别定义在 HttpError.js 与 NetworkError.js。HttpGetStore(options)HttpStore的只读版本。它继承HttpStore的全部读能力但set()为空操作HttpGetStore.jsget()遇到HttpError或NetworkError时不抛出而是吞掉错误并返回null同时只发出一次process.emitWarning警告提示无法连接 HTTP 缓存HttpGetStore.js。这正是它适合放在开发机上的原因远程缓存不可用时开发构建不至于失败只是退化为本地计算。四、缓存键的计算与命中流程源码级理解缓存的粒度有助于正确配置。Metro 的缓存单位是单个模块的转换结果键的构造在 Transformer.js 中完成全局缓存键globalCacheKey基于cacheVersion、projectRoot与 transformer 配置计算Transformer.js若cacheStores为空数组缓存被禁用则全局缓存键为空串Cache.isDisabled在 Cache.js 中定义。部分键partialKey由全局缓存键、项目相对路径POSIX 分隔、asset URL 路径、以及dev、platform、minify、type等转换选项经stableHash计算得到Transformer.js。完整键fullKeypartialKey拼接模块内容的 SHA-1 后得到Transformer.js。也就是说同一个文件只要内容SHA-1没变、转换选项没变就能命中缓存而stableHash使用带 canonicalize 的 JSON 序列化加 MD5 实现机器无关的稳定哈希见 stableHash.js保证了不同机器之间缓存键一致、可共享远程缓存。命中后的流程是cache.get(fullKey)命中则直接使用结果未命中则交给workerFarm.transform()重新转换转换完成后cache.set()以 fire-and-forget 方式异步写回所有未命中该键的 storeTransformer.js。读取与写入失败会分别通过 reporter 上报cache_read_error/cache_write_error事件。五、自定义缓存存储cacheStores接收的是实现了以下接口的类的实例官方文档 docs/Caching.md 原样给出仓库中的类型定义见 types.jsinterface CacheStoreT: Buffer | JsonSerializable { // 从缓存读取条目未找到时返回 null get(key: Buffer): ?T | Promise?T; // 写入缓存条目只读存储可空操作 set(key: Buffer, value: T): void | Promisevoid; // 清空缓存如可行否则空操作 clear(): void | Promisevoid; } type JsonSerializable /* Any JSON-serializable value */;实现时有几点需要严格遵守缓存条目的值要么是Buffer实例要么是 JSON 可序列化的值内部结构两者均不指定对同一个缓存键get()必须返回与set()时相同类型的值例如不能写入 Buffer 却按 JSON 解析返回set与clear是“尽力而为”的只读存储直接空操作即可按需提供name属性或依赖类名用于Cache的日志标识Cache.js。如果你的远程存储无法直接支持按 key 读写例如对象存储需要额外索引可以仿照HttpGetStore的容错思路读取失败时返回null并告警让缓存不可用时退化为本地计算写入失败时则交由Cache.set()内部捕获——源码中set()会收集所有 store 的写失败并抛出AggregateErrorCache.js因此自定义 store 的set()建议在需要时自行吞掉可恢复错误避免影响主构建流程。六、实战完整的本地 远程缓存配置将以上内容组合起来一个面向团队场景的完整metro.config.js大致如下// metro.config.js const os require(node:os); const path require(node:path); module.exports { cacheStores: ({ FileStore, HttpStore, HttpGetStore }) [ // 1. 本地缓存优先目录置于系统临时目录 new FileStore({ root: path.join(os.tmpdir(), metro-cache), }), // 2. CI/写端完整的读写远程缓存客户端 new HttpStore({ endpoint: https://cache.example.com/metro, timeout: 5000, // HTTPS 双向认证所需选项如需要 // cert: ..., ca: ..., key: ..., // 可选读、写使用不同端点 // getOptions: { endpoint: https://cache-ro.example.com/metro }, // setOptions: { endpoint: https://cache.example.com/metro }, }), // 3. 开发机/只读端远程缓存不可用时静默降级 // new HttpGetStore({ endpoint: https://cache.example.com/metro }), ], };配置落地后CI 环境定期执行metro build entry --out bundle.js可配合--platform、--minify等选项用HttpStore将转换结果灌入团队缓存开发环境使用HttpGetStore或去掉写端 store构建时优先命中本地FileStore未命中则从远程缓存读取远程不可用时静默回退版本升级升级 Metro 或改变 transformer 配置后通过提升cacheVersion让旧缓存整体失效避免错误复用旧格式的缓存产物。结语Metro 的缓存体系本质上是“本地文件缓存兜底 可插拔的远程缓存提速”cacheStores决定存储后端cacheVersion与模块 SHA-1 决定缓存键的有效性Cache类决定多存储间的读写语义。把握住“本地优先、远程在后、读取失败降级”这三条原则再结合metro-cache内置实现与自定义接口就能在单机开发、CI 预热、团队共享三种场景下充分发挥 Metro 缓存的加速能力。相关实现细节可继续研读 packages/metro-cache、Transformer.js 以及 docs/Configuration.md 中的cacheStores小节。赞分享构建工具移动开发CLI【免费下载链接】metro The JavaScript bundler for React Native项目地址https://gitcode.com/gh_mirrors/me/metro点击查看免费下载相关推荐sbt构建缓存终极指南远程缓存和本地缓存配置详解sbt构建缓存终极指南远程缓存和本地缓存配置详解 在Scala项目开发中 sbt构建缓存 是提升开发效率的关键技术。无论是个人开发还是团队协作合理配置缓存构建工具Turborepo 缓存实战指南Monorepo 本地与远程构建缓存配置与调优Turborepo 缓存实战指南Monorepo 本地与远程构建缓存配置与调优 本文以 agents24 仓库中 developer essentials 插AI 插件AI 技能开发工具Cangjie/HarmonyOS-Examples 缓存机制实现本地缓存与网络缓存Cangjie/HarmonyOS Examples 缓存机制实现本地缓存与网络缓存 引言为什么需要缓存机制 在移动应用开发中缓存Cache机制是提示例工程上一篇突破移动边界Winlator技术对比与跨平台解决方案深度解析下一篇xh终极HTTP请求工具比HTTPie更快更友好的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考