首页
学习
活动
专区
圈层
工具
发布

Yarn

修改于 2026-09-07 18:26:28
7
概述

Yarn 是一款面向 JavaScript 和 Node.js 生态的包管理器,由 Meta(原 Facebook)于 2016 年联合 Exponent、Google、Tilde 共同创建,用于解决早期 npm 在安装速度、依赖确定性和离线能力上的不足。它通过 lockfile 锁定依赖版本、并行下载、全局缓存、workspaces 等机制管理项目依赖,且始终复用 npm 注册表中的同一批包,因此可在不重写 package.json 的前提下与 npm 平滑切换。经过多年发展,Yarn 分化为两条版本线:处于维护模式的 Yarn Classic(v1.x)与持续演进的 Yarn Modern(又称 Yarn Berry,v4.x),后者引入了 Plug'n'Play、零安装、约束引擎和插件系统等现代化能力。

一、Yarn 的 lockfile 是怎么保证依赖版本一致的?

1. 用 yarn.lock 记录完整依赖树

  • Yarn 在项目中维护一个名为 yarn.lock 的文件,记录依赖树中每一个包的确切版本、解析地址和完整性校验值
  • 该文件由 Yarn 自动生成和维护,开发者不应手动编辑;执行 yarn addyarn removeyarn upgrade 等操作时,它会随之更新
  • 安装时 Yarn 只读取项目顶层的 yarn.lock,忽略各依赖内部可能存在的同名文件,因为顶层文件已包含整棵依赖树所需的全部信息

2. 通过三重机制保证确定性安装

  • 版本锁定:每个依赖都被锁定到确切版本号,消除 semver 范围(如 ^1.2.0)的歧义
  • 地址固化:存储完整的 tarball 下载地址,避免注册表解析漂移
  • 完整性校验:每个包条目都带有 SHA-512 校验哈希,下载后逐字节比对,确保内容未被篡改或损坏

3. 跨机器复现同一结果

  • 只要使用相同版本的 Yarn 并配合同一份 yarn.lock,不同机器上会得到完全一致的依赖树
  • yarn.lock 应纳入版本控制(如 Git),使团队成员、CI 服务器都能安装出相同的依赖
  • 需要注意的是,lockfile 只保证"解析结果一致",具体的 node_modules 目录布局还依赖 Yarn 自身的提升算法,因此不同 Yarn 版本之间可能出现目录结构差异

二、Yarn 的安装速度为什么比 npm 快很多?

1. 并行下载架构

  • Yarn 会先构建依赖关系图,分析各包之间的依赖顺序,再对互不依赖的包发起并行下载
  • 相比之下,早期 npm 采用串行模型,一个包下载完成后才开始下一个
  • 当项目依赖层级较深、依赖数量较多时,并行下载的优势更为明显

2. 安装流程的算法优化

  • Yarn 的安装流程为:解析 yarn.lock → 计算所有包的哈希值(基于解析地址与完整性校验)→ 检查缓存中是否已有对应 zip 包 → 命中则跳过下载、直接建立链接 → 并行执行 postinstall 脚本
  • 关键优化在于把"是否需要重装"的判断提前到了哈希计算阶段,且缓存采用 zip 格式(解压速度快于 tar.gz)
  • npm 后续也重写了安装管线并引入并行化,因此在冷安装场景下两者差距已明显缩小
  • 具体而言,npm 11(随 Node.js 24 LTS 于 2025 年 10 月推广)对安装流程做了显著优化,官方基准显示中等规模项目(约 500 个依赖)的安装耗时较 npm 10 缩短约 38%

3. 重复安装时的持久优势

  • Yarn 的速度优势主要体现在重复安装(热缓存)场景,这也是日常开发和 CI 中最常见的情况
  • 在 Yarn Modern 的 Plug'n'Play 模式下,由于无需重建 node_modules 目录树,热安装和 lockfile 命中场景下的速度领先较为显著
  • 而 Yarn Classic 相对 npm 11 的速度优势已经很小,因为 npm 11(2024 年 12 月发布、2025 年随 Node.js 24 LTS 大规模推广)的安装管线优化基本补齐了并行化短板

三、Yarn 的缓存机制是怎么减少重复下载的?

1. 全局缓存与内容寻址

  • 每个包首次从远程下载后,都会以内容哈希为标识存入本地缓存
  • 再次安装相同版本的依赖时,Yarn 直接从缓存复制文件,而非重新发起网络请求
  • Yarn Modern 默认将缓存配置为项目本地(.yarn/cache),便于纳入版本控制;也可通过 enableGlobalCache: true 切换为所有项目共享的全局缓存

2. 离线缓存与离线镜像

  • 离线缓存是 Yarn 的一项核心特性,即使网络中断也能正常安装
  • 每个包在缓存中只保存一份 zip 归档,体积小、适合提交到代码仓库
  • 结合 Plug'n'Play,文件可直接从 zip 归档中读取,因此缓存无法被禁用;如需清理可运行 yarn cache clean

3. 缓存完整性校验

  • 由于归档的校验和存储在 lockfile 中,任何缓存损坏都会在安装时被检测到
  • Yarn 会提示用户解决问题——要么删除损坏的文件,要么更新校验和(后者仅面向高级用户在极特殊情况下使用)
  • 这种机制保证了即便缓存出问题,也不会影响项目依赖的正确性

四、Yarn 在依赖安全方面做了哪些防护?

1. 完整性校验与漏洞审计

  • Yarn 在安装时使用 yarn.lock 中的校验和对每个包进行验证,降低被篡改或损坏的风险
  • 可通过 yarn npm audit 命令检查依赖树中的已知漏洞,并支持自动修复
  • Yarn 还内置许可证检查能力,可对依赖的开源协议做合规筛查

2. Hardened Mode(强化模式)

  • Yarn Modern 提供 Hardened Mode,会校验 lockfile 中的解析结果是否与声明的范围所能解析到的内容一致,以及包元数据是否与注册表数据匹配
  • 该模式可防止 pull request 中对 lockfile 的恶意篡改
  • 启用方式为设置环境变量 YARN_ENABLE_HARDENED_MODE=1

3. 不可变安装与严格隔离

  • yarn install --immutable 会在 lockfile 可能被修改时直接失败,防止 CI 中发生意外更新
  • 在 Plug'n'Play 模式下,代码无法访问未显式声明的依赖,从而避免幽灵依赖带来的攻击面
  • 这些机制与 npm 的 provenance 证明、pnpm 的默认脚本阻断等方案思路相近,但实现路径各有不同

五、Yarn 的零安装功能是怎么让团队不用跑 install 的?

1. 把安装产物提交到版本控制

  • 零安装(Zero Installs)的核心思路是:将所有安装产物纳入版本控制,克隆仓库后即具备运行项目的全部文件,无需执行 yarn install
  • 切换分支时,版本控制系统在 checkout 的同时也就完成了依赖的更新
  • 这一模式尤其适合高速迭代的项目,能避免因忘记重装依赖而导致工具崩溃的问题

2. 依赖 Plug'n'Play 与离线缓存两个特性

  • 单纯提交 node_modules 不可行——其中包含成千上万个文件,频繁增删依赖时会被随意移动,产生巨大的 diff
  • Yarn 的做法是:在 Plug'n'Play 模式下生成单一的 .pnp.cjs 映射文件,并把离线缓存(.yarn/cache)提交到仓库
  • 由于 PnP 加载器的内容在任何机器上都完全相同,且离线缓存包含了加载器引用的所有文件,git checkout 实际上就等价于一次"准安装"

3. 适用边界与注意事项

  • 添加或删除带有原生依赖(native dependencies)的包时,仍需运行 yarn install,因为这类包依赖的文件无法像 Node.js 脚本那样直接从 zip 归档中求值
  • 这类包在实践中较少见、更新也不频繁,且 Yarn 会在遗忘时给出明确的错误提示
  • 建议将缓存文件以未压缩形式存储,因为实测显示未压缩状态下的仓库体积反而更小、Git 的 delta 计算也更高效

六、Yarn 的约束引擎能帮团队做哪些事?

1. 用声明式规则统一 monorepo 依赖

  • 约束(Constraints)是 Yarn 提供的一种在多个 workspace 间强制执行通用规则的能力
  • 通过在项目根目录放置 yarn.config.cjs(或 .ts.mjs)文件,导出 constraints 方法即可定义规则
  • 典型用途包括:确保所有 workspace 使用同一版本的 React、强制每个包都声明 license 字段、禁止使用特定依赖等

2. 声明式模型与自动修复

  • 约束采用声明式模型:开发者只需声明期望的状态,Yarn 负责检查实际是否匹配
  • 运行 yarn constraints 会在规则不满足时抛出错误;运行 yarn constraints --fix 则会尝试自动修复问题
  • 正因为是声明式的,规则内部不应自行判断当前值再决定是否更新,而应直接调用 update 等方法声明目标状态

3. 相比静态检查的独特优势

  • 与 ESLint 之类的静态检查不同,约束引擎能够访问整个项目的依赖树
  • 因此它可以实施静态分析难以实现的规则,例如循环依赖检测、跨包版本一致性校验等
  • Yarn 还提供了 @yarnpkg/types 类型包,便于用 TypeScript 编写约束配置

七、Yarn 的插件系统是怎么扩展功能的?

1. 模块化架构下的插件设计

  • Yarn Modern 围绕一个精简的核心(@yarnpkg/core)重新设计,大部分实际功能都通过插件实现,甚至连 yarn addyarn install 本身也是预装插件
  • 插件在运行时被 Yarn 加载,可以注入新行为,并能直接使用 Yarn 提供的核心 API(如 @yarnpkg/core),无需在自己的依赖中重复声明

2. 插件可扩展的能力范围

  • 新增解析器(resolver):将依赖范围(如 ^1.2.0)转换为完全限定的包引用
  • 新增获取器(fetcher):从远程注册表或本地磁盘等数据源获取包数据
  • 新增链接器(linker):生成安装所需的文件,例如 PnP 链接器生成 .pnp.cjs,node-modules 链接器生成传统目录结构
  • 新增命令与钩子:插件可注册自定义 CLI 命令,并监听安装生命周期中的各类事件(如 afterAllInstalled

3. 官方插件与社区生态

  • 官方插件包括 constraints(跨 workspace 强制 lint 规则)、typescript(自动补充 @types 依赖)、version(monorepo 发布管理)、workspace-tools(workspaces foreach 等命令)、interactive-tools(图形化交互升级)等
  • 社区贡献的插件集中维护在 contrib 列表中,使用前应自行审查其质量与安全性
  • 编写插件可使用 @yarnpkg/builder 工具将 TypeScript 源码打包为单文件发布

八、Yarn 的 workspaces 怎么管理 monorepo 里的多个包?

1. 通过 glob 模式声明 workspace

  • 在根目录的 package.json 中添加 workspaces 字段,列出各子包的相对路径(支持 glob 模式,如 "packages/*"
  • 每个被匹配的目录只要包含自己的 package.json,就会成为一个独立的 workspace
  • 即使项目只有一个包,根 workspace 也始终存在,作为默认 workspace

2. workspace 间的依赖与共享

  • 内部包之间可通过特殊的 workspace: 协议相互引用(如 "@my-org/utils": "workspace:^"),该协议在发布时会被透明替换为实际版本号
  • 还可通过 catalog: 协议在多个 workspace 间共享依赖范围
  • Yarn 会把共享依赖提升到根目录,每个版本只安装一次,从而节省磁盘空间并加快安装

3. 配套的 monorepo 工具链

  • yarn workspaces foreach 可跨多个 workspace 并行运行同名脚本,-A 选中全部、-p 并行、-t 按拓扑顺序(依赖优先)执行
  • yarn workspaces focus 可聚焦安装指定 workspace 及其传递依赖所需的包,跳过其他无关内容
  • 约束引擎、workspace profiles 等能力可与 workspaces 配合,进一步保证 monorepo 内各包遵循一致的规范

九、Yarn 怎么查看和分析项目的依赖树?

1. yarn why 追溯依赖来源

  • yarn why 命令会打印某个包出现在依赖树中的确切原因,即是哪条依赖链最终引入了它
  • 可指定版本或范围(如 yarn why lodash@^3)来查明为何项目依赖了某个特定版本
  • 这对诊断不需要的传递依赖、排查版本冲突、发布前审计 node_modules 都很有帮助

2. 递归与结构化输出

  • 在 Yarn Modern 中,加上 -R, --recursive 参数后,会深入列出每个 workspace 通向该依赖的所有路径
  • --json 参数可将输出格式化为 NDJSON 流,便于脚本处理
  • --peers 参数会同时打印与指定名称匹配的 peer dependencies

3. 与其他分析方式互补

  • 除命令行外,也可直接阅读 yarn.lock 中扁平化的依赖条目来理解包之间的关系
  • 对于希望可视化依赖图的场景,可借助第三方工具渲染 lockfile
  • 若发现某个"根"依赖并非项目的直接依赖,可继续对它运行 yarn why,直到追溯到项目的直接依赖为止

十、Yarn Classic 和 Yarn Modern 到底有什么区别?

1. 两条版本线的定位差异

  • Yarn Classic(v1.x):2016 年创建的原始版本,最后版本为 1.22.22,目前处于冻结状态(frozen),不再新增功能,仅做安全修补
  • Yarn Modern(代号 Berry,当前最新稳定版为 4.18.0,截至 2026 年 8 月):2020 年起推出的近乎重写版本,是当前活跃开发的版本线,官方明确引导新项目直接使用 Modern
  • 两者品牌名统一为 "Yarn",Classic 与 Modern 是同一产品下的两条版本线,而非两个独立产品

2. 核心能力的代际差别

维度

Yarn Classic(v1.x)

Yarn Modern(v4.x)

模块链接方式

传统的 node_modules 目录

默认 Plug'n'Play,用 .pnp.cjs 映射替代目录

配置文件

.yarnrc / .npmrc

统一的 .yarnrc.yml(YAML 格式)

插件系统

仅限内置功能

完整的模块化插件 API 生态

约束引擎

不支持

基于 JavaScript 的约束引擎

零安装

不支持

支持(PnP + 离线缓存)

一次性运行包

yarn global

yarn dlx

3. 迁移与兼容性

  • 从 Classic 迁移到 Modern 时,原有的 yarn.lock 会被自动转换,通常几天即可完成中等规模项目的迁移
  • Modern 保留了 node-modules 链接器,当某些工具不兼容 PnP 时,可在 .yarnrc.yml 中设置 nodeLinker: node-modules 回退到传统目录结构
  • 部分命令发生了变化,例如 yarn audit 改为 yarn npm audityarn create 改为 yarn dlx create-xxx

十一、Yarn 和 npm 在依赖提升策略上有什么不同?

1. npm 的扁平化提升

  • npm 采用扁平化(flat)的 node_modules 结构,尽可能多地把包提升到顶层目录以减少重复
  • 这种做法简单且兼容几乎所有工具,但会带来幽灵依赖(phantom dependency)问题:代码可以 require 到从未在 package.json 中声明、却恰好被提升到顶层的包
  • 一旦传递依赖调整了自己的依赖,这类未声明的引用就可能突然失效

2. Yarn 的提升与隔离策略

  • Yarn Classic 同样使用扁平化的 node_modules,提升逻辑与 npm 相近,也存在类似的幽灵依赖风险
  • Yarn Modern 的 Plug'n'Play 模式则彻底摒弃了 node_modules,改用 .pnp.cjs 映射文件直接回答"某个包在哪里"的问题,从机制上消除了幽灵依赖
  • 在 node-modules 链接器模式下,Yarn 也提供了 nmHoistingLimits 配置来限制提升范围,收紧依赖可见性

3. 确定性保障的差异

  • Yarn 很早就强制严格的确定性,Modern 的实现更严格——默认情况下若 lockfile 会被修改就直接拒绝安装,把意外变更拦截在生产之前
  • npm 5+ 也采用了 lockfile 机制,但其确定性更多依赖 package-lock.json 对目录形状的完整描述
  • 在实际工程中,Yarn 的提升算法会综合考虑 optionalDependencies、devDependencies 等因素,即使它们未被安装也会影响普通依赖的提升位置,从而减少开发与生产环境的布局差异

十二、Yarn 和 pnpm 相比各自有什么优劣?

1. 存储架构的根本不同

  • pnpm:采用内容寻址的全局 store,每个包版本在磁盘上只存一份,再通过硬链接接入各项目的 node_modules,并用符号链接构建严格的依赖树
  • Yarn Modern(PnP):完全不写 node_modules,而是生成 .pnp.cjs 映射文件,从压缩缓存中直接定位包
  • npm / Yarn Classic:构建扁平化的 node_modules,通过提升来减少重复

2. 性能与磁盘占用对比

指标

pnpm

Yarn Modern(PnP)

Yarn Classic

冷安装速度

快(实测常为最快档)

快(热安装场景优势明显)

与 npm 接近

磁盘占用

最低(多项目共享 store)

较低(PnP 缓存去重)

较高(扁平 node_modules)

幽灵依赖防护

默认严格隔离

PnP 模式天然隔离

依赖提升,存在风险

monorepo 支持

过滤与拓扑排序成熟

workspaces + 约束引擎

基础 workspaces

3. 选型建议

  • 追求最佳综合平衡(速度、正确性、monorepo 体验)时,pnpm 是多数新项目的推荐起点
  • 团队已投入 Plug'n'Play 和零安装生态、或需要约束引擎这类强治理能力时,Yarn Modern 更具优势
  • 存量项目若仍在 Yarn Classic 上稳定运行,迁移成本可控,但若不打算升级到 Modern,则其相对 npm 11 的速度优势已不明显

十三、Yarn 更适合用在什么类型的项目里?

1. 大型 monorepo 与企业级项目

  • Yarn 是最早原生支持 workspaces 的包管理器之一,特别适合核心包 + 多个扩展包的组织方式
  • 约束引擎能在整个 monorepo 范围内强制依赖版本一致、禁用特定包、统一字段规范,相当于针对依赖树的"lint 工具"
  • 零安装能力让大规模团队在切换分支、扩容 CI 时无需反复执行安装步骤

2. 对安装速度和可复现性敏感的场景

  • 在 CI/CD 流水线中,Yarn Modern 的热安装和零安装能显著缩短依赖准备时间
  • lockfile 与完整性校验保证了开发、测试、生产环境依赖的高度一致,降低"在我机器上能跑"的问题
  • 离线缓存使得受限网络环境或注册表故障时仍能稳定构建

3. 需要灵活扩展工具链的团队

  • 插件系统允许团队在不 fork 主程序的情况下定制工作流,例如自动化发布、依赖治理、编辑器集成等
  • 对于工具链尚未完全适配 PnP 的项目,可随时通过 node-modules 链接器回退到传统目录结构,兼顾新特性与兼容性
  • 若项目对磁盘占用极度敏感或需要最严格的依赖隔离,也可评估 pnpm 等同类方案作为补充
相关文章
  • YARN
    1.8K
  • YARN
    1.5K
  • Yarn篇--搭建yarn集群
    2.7K
  • Yarn原理
    715
  • yarn详解
    2.6K
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
领券