跳至主要内容

问题与解答

为什么 npm 上的 yarn 包仍然停留在 1.x?

自 2019 年以来,Yarn 的现代版本不再在 npm 上分发。

原因很简单:因为 Yarn 并未随 Node.js 一起分发,所以许多人依赖于类似于 npm install -g yarn 的内容作为其镜像构建的一部分。这意味着任何重大更改都会影响使用此模式的每个人,并中断其部署。

因此,我们决定弃用 yarn npm 包,仅将其用于所需的少数几个 1.x 维护版本。Yarn 现在可通过我们的网站直接安装,可通过 Corepackyarn set version 安装。

您为什么要升级到 Yarn Modern?

虽然 Yarn Classic 系列 (1.x) 仍然是 JavaScript 生态系统的支柱,但我们建议尽可能升级。这是为什么?

  1. 新功能:除了您已经习惯的经典功能之外,您还将发现新的功能(yarn dlx内置 patch: 协议,...),Modern 提供的插件可通过 变更集约束工作区,... 扩展 Yarn 的功能集。

  2. 效率:Modern 新安装策略,导致项目仅为过去自身的一小部分;例如,在默认配置下,库存 CRA 工件现在仅占用 45MB,而不是 237MB。 性能 也得到了提升,现在大多数安装即使在极大型项目上也只需几秒钟。我们甚至让 零秒 成为可能!

  3. 可扩展性:Modern 的架构允许您根据需要构建自己的功能。不再需要等待我们实现您梦寐以求的功能,您现在可以根据自己的规格自己动手!专注的工作空间、自定义安装、项目验证,...

  4. 稳定性:Modern 历经多年维护 Classic 的经验;它使我们最终修复了某些功能实现方式的长期设计问题。工作空间现在是核心组件,解析管道已经过简化,数据结构更有效率...因此,Modern 不太可能受到错误假设和其他设计缺陷的影响。

  5. 面向未来:我们投资 Modern 的一个重要原因是,我们注意到在 Classic 上构建新功能变得困难 - 每个更改都太可能产生无法预见的后果。Modern 架构从我们的错误中吸取教训,并被设计为允许我们以更快的速度构建功能 - 我们的新速度证明了这一点。

您应该期望从 Classic 迁移到 Modern 有多容易?

一般来说,需要处理一些主要事情

  1. 设置格式已更改。我们不再读取 .npmrc.yarnrc 文件,而是从 .yarnrc.yml 文件 中获取设置。

  2. 一些第三方包没有正确列出其依赖项,需要通过 packageExtensions 设置来提供帮助。

  3. 对文本编辑器的支持非常好,但您需要运行 SDK 文档 中列出的单次设置。

  4. 一些工具(主要是 React Native 和 Flow)需要通过将 nodeLinker 设置为 node-modules 来降级到 node_modules 安装策略。TypeScript 没有这个问题。

大多数项目只会遇到这四个问题,所有问题都可以在一个下午的工作时间内修复。有关更详细的说明,请参阅详细的迁移指南

哪些文件应被 gitignored?

如果您正在使用零安装

.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

如果您未使用零安装

.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

如果您有兴趣了解有关每个文件的更多信息

  • .yarn/cache.pnp.* 可以安全地忽略,但您需要运行 yarn install 以在每次分支切换之间重新生成它们 - 否则是可选的,请参阅 零安装

  • .yarn/install-state.gz 是一个优化文件,您永远不必提交。它只是存储项目的准确状态,以便后续命令可以启动,而无需重新解析您的工作区。

  • .yarn/patches 包含您使用 yarn patch-commit 命令生成的补丁文件。您始终希望它们在您的存储库中,因为它们对于安装您的依赖项是必需的。

  • .yarn/plugins.yarn/releases 包含当前存储库中使用的 Yarn 版本(由 yarn set version 定义)。您需要保持它们的版本(如果两个工程师使用具有不同功能的不同 Yarn 版本,这可以防止潜在问题)。

  • .yarn/sdks 包含由 @yarnpkg/sdks 生成的编辑器 SDK。是否将其保留在您的存储库中取决于您;如果不保留,您需要在新克隆中再次遵循编辑器程序。有关更多详细信息,请参阅 编辑器 SDK

  • .yarn/unplugged 通常应该始终被忽略,因为它们通常保存特定于机器的构建工件。然而,忽略它可能会阻止 零安装 工作(要防止这种情况,将 enableScripts 设置为 false)。

  • .yarn/versions版本插件 用于存储包发布定义。您需要将其保留在存储库中。

  • yarn.lock 应始终存储在您的存储库中(即使您开发库)。

  • .yarnrc.yml(及其较早版本 .yarnrc)是配置文件。它们应始终存储在您的项目中。

提示:您还可以添加一个 .gitattributes 文件,以将发布和插件包标识为二进制内容。这样,每次添加或更新它们时,Git 不会显示大量差异

/.yarn/releases/** binary
/.yarn/plugins/** binary

Yarn 是否支持 ESM?

是的。

首先,请记住 Yarn 支持 node-modules 安装策略,它安装包与 npm 完全相同。因此,如果 Yarn 不支持 ESM,则没有任何东西支持。如果您听到有人说它不支持,他们实际上指的是“Yarn PnP 不支持 ESM” - 但它确实支持,自 3.1 以来一直如此。

因此,仅此一项就应该回答您的问题。但是,如果您想了解有关 PnP 和 ESM 故事的更多详细信息,那么我们首先讨论 ESM 本身。ESM 是两件事:从本质上讲,它是在 ES2015 中起草的一项规范。然而,没有引擎立即实现它:此时该规范几乎只是一个语法占位符,下面没有任何具体内容。从 2019 年底开始,Node 才最终获得对原生 ESM 的支持,而无需实验性标志。但这种支持有一个主要警告:没有 ESM 加载器。加载器允许项目告诉 Node 如何在磁盘上找到包和模块。您可能知道其中一些:@babel/registerts-nodeJest 的模拟Electron 等等。

与 CommonJS 不同,ESM 模块解析管道旨在完全与外部隔离开来,例如,多个线程可以共享同一个解析器实例。这意味着,在没有官方加载器支持的情况下,不可能支持备用解析策略 - 对解析基本元素进行热修补不再可行,因此所有这些项目实际上根本无法支持 ESM。这只能意味着:ESM 尚未准备好。是的,它得到了本机支持,但鉴于它破坏了生态系统中相当大的一部分,并且没有任何替代方案,它还不能成为一个合理的标准 - 现在还不行。

幸运的是,Node 发现了这个问题,开始开发加载器,并发布了第一个迭代版本。快进到今天,Node 加载器仍然处于 繁重的工作 中(并且已经改变了不止一次,如这个“实验性”注释所强调的),但已经允许我们起草一个与 ESM 兼容的 PnP 加载器的第一个实现,我们在 3.1 中发布了它。凭借这些经验,我们开始为 Node 加载器工作组做出贡献,不仅是为了帮助 Yarn 自己的用例,也为了帮助其他可能效仿我们的项目的用例。

加载器还不够完善,在它们完善之前,不推荐仅使用 ESM 的包,但有办法解决这个问题,而且随着我们共同努力,我们会实现这一目标。我们只需要小心不要在朝着目标前进时将人们推到一边。

是否应该将锁文件提交到仓库?

是的。

锁文件应该始终与项目源一起存储 - 无论您编写的是独立应用程序还是分布式库。

反对在仓库中签入锁文件的持续争论是关于意识到针对库最新版本的潜在问题。持这种观点的人认为,锁文件的存在会阻止贡献者看到此类问题,因为所有依赖项都已锁定,并且在使用者安装库并使用较新(且不兼容)的依赖项之前,它们看起来都很好。

尽管很有吸引力,但这种推理有一个致命的缺陷:从仓库中删除锁文件并不能阻止此问题发生。特别是

  • 活跃的贡献者不会获得新版本,除非他们明确删除其安装工件(node_modules),这可能不会经常发生。因此,有问题的依赖项升级将主要由新贡献者发现,这不会带来良好的首次体验,并且可能会阻止贡献。

  • 即使你假设每周都运行全新安装,你的升级也不会容易恢复 - 一旦你测试了最新软件包,你将不会针对较旧的软件包进行测试。兼容性问题仍然存在,只是针对以前可以使用但不再测试的软件包。换句话说,通过始终测试最新的 semver 版本,你不会看到是否意外开始依赖以前不可用的功能。

当然,这些要点只是问题的一部分 - 缺少锁文件还意味着存储库中缺少关键状态信息。几个月后,你或你的贡献者想要修复旧项目中的一个问题,你甚至可能无法再构建它,更不用说改进它了。

锁文件始终应保存在存储库中。持续集成测试是个好主意,但应留给持续集成系统。例如,Yarn 本身针对主要开源框架和工具的最新版本运行每日测试,这使我们能够快速发现与最新版本有任何兼容性问题,同时仍然保证每个贡献者在使用该项目时都能获得一致的体验。DependabotRenovate 也是很好的工具,可以为你跟踪依赖项更新。

如何在工作区之间共享脚本?

鲜为人知的 Yarn 特性:任何名称中带有冒号的脚本(build:foo)都可以从任何工作区调用。另一个鲜为人知的特性:$INIT_CWD 始终指向运行脚本的目录。综合起来,你可以编写可以这样重复使用的脚本

{
"dependencies": {
"typescript": "^3.8.0"
},
"scripts": {
"g:tsc": "cd $INIT_CWD && tsc"
}
}

然后,从包含其自己的 tsconfig.json 的任何工作区,你将能够调用 TypeScript

{
"scripts": {
"build": "yarn g:tsc"
}
}

或者如果你只想从根工作区使用 tsc

{
"scripts": {
"build": "run -T tsc"
}
}

如果你想在项目的根目录中运行脚本

{
"scripts": {
"build": "node ${PROJECT_CWD}/scripts/update-contributors.js"
}
}

Yarn 是否由 Facebook 运营?

否。

尽管 Yarn 的第一个版本是由 Sebastian McKenzie 在 Facebook 工作时实现的,但最初的设计收到了来自其他多家公司的反馈(例如 Tilde 通过 Yehuda Katz),并且该项目被纳入其自己的 GitHub 组织 中。Facebook 在接下来的几年中一直对其进行投资(主要是因为它被证明是 RN 生态系统中至关重要的一部分),但主要贡献也来自开源。

如今,活跃的开发团队完全由非创始人公司雇用的人员组成。当然,Facebook 员工仍然可以为该项目提供贡献,但他们将与其他人经历相同的审查流程。

为什么是 registry.yarnpkg.com?Facebook 会追踪我们吗?

否。

当 Yarn 被创建时,npm 注册表曾经通过 Fastly 提供服务。这显然影响了安装性能,因此最初的团队决定与 Cloudflare 合作并设置一个 反向代理,它会在返回请求之前简单地更好地缓存请求。此设置甚至没有我们这边的后端。

在某个时候,npm 也切换到了 Cloudflare,我们关闭了代理,用 CNAME证明)替换了它。出于可靠性原因,我们仍然保留主机名 - 虽然有理由认为 Yarn 域名将一直维护到 Yarn 被使用,但 npm 域名不一定也是如此。如果主来源不可用,这使我们能够重定向到注册表的只读副本。

虽然我们确实收集了一些基本的 客户端遥测,但没有任何 http 日志可以到达 Yarn 项目基础设施 - 更不用说 Facebook 了,它无法控制该项目(另请参阅,Yarn 是否由 Facebook 运营?)。

registry.yarnpkg.com 的查询返回 404/500/...;它宕机了吗?

否。

上一节 中所述,Yarn 注册表只是 npm 注册表的 CNAME。由于我们甚至没有后端,因此任何服务器错误只能来自 npm 注册表,因此应向他们报告并在其 状态页面 上进行监控。

Yarn 是否比其他包管理器更快?

耸肩 🤷‍♀️

在 Yarn 发布时,Yarn 实际上比一些竞争对手快得多。不幸的是,我们未能强调性能并不是我们继续开发 Yarn 的主要原因。性能时好时坏,因此虽然我们非常快,但并不是因为我们做得非常好,而是因为竞争对手的实现存在严重错误。当该错误得到修复时,我们的错误沟通变得更加明显,因为有些人认为 Yarn 与性能息息相关。

简单来说,我们的差异在于我们的优先事项。不同的项目做出不同的权衡,这里发生的事情正是如此。我们优先考虑工作区,因为我们认为单一仓库提供了显着的价值。我们投入了大量资源来推动即插即用(包括通过 对第三方项目的数十项贡献),因为我们认为这对生态系统很重要。这是主要区别:我们根据项目路线图做出自己明智的决策。

速度是相对的,也是暂时的状态。流程、路线图和核心价值观才是坚持不懈的。

即使我不使用 Plug'n'Play,为什么 TypeScript 仍会被修补?

鉴于 PnP 是不同于 Node 的解析器标准,重新实现 require.resolve API 的工具需要添加一些逻辑来考虑 PnP 解析。虽然各种项目都已这样做(例如 Webpack 5 现在开箱即用地支持 PnP),但少数项目仍对此犹豫不决。在 TypeScript 的情况下,我们启动并持续维护一个 pull request,但 TypeScript 团队仍需接受它。为了解除我们用户的封锁,我们决定使用我们新的 patch: 协议,将此确切的 pull request 自动应用到下载的 TypeScript 版本。

现在出现了一个问题:即使禁用 Plug'n'Play,我们为什么仍应用此修补程序?主要原因是 Yarn 打算提供一致的行为。一些设置包括在开发期间使用 node_modules 链接器(以避免必须设置编辑器 SDK)和在生产中使用 PnP(以提高安装速度)。如果我们仅在启用 PnP 时应用修补程序,那么包缓存将变得不同,例如,这将破坏不可变安装。

我们可以通过一个开关使其可配置,但最终我们决定不值得进行额外的配置

  • 如果未启用 PnP,TypeScript 修补程序将不起作用,因此这不会影响你的工作(如果确实如此,请打开一个问题)
  • 我们希望最终有一天将此 PR 纳入 TypeScript,因此我们能获得的关注越多,我们的信心就越高
  • 自 Yarn 3+ 以来,内置修补程序失败将被简单地忽略,并回退到原始源