[译] Yarn 官方介绍: 一款新的 JavaScript 包管理器

[译] Yarn 官方介绍: 一款新的 JavaScript 包管理器 Original: Yarn: A new package manager for JavaSc

[译] Yarn 官方介绍: 一款新的 JavaScript 包管理器


这两天被 Yarn 刷屏了?对于新工具,范范地道听途说或浅尝辄止,不如静下心来听听作者的心路历程。我读了多篇文章,感觉说得最清楚的还是 Facebook 发表的这篇官方介绍,于是翻译出来分享给大家。

在谈论 Yarn 时,有人在谈论前端圈的 “造轮子” 风气,有人在谈论 Facebook 工程师的 PR 能力,有人在谈论网络速度……但读完这篇文章之后,我看到的是前端工程、是开发体验、是思维方式。相信不论是一线工程师还是架构师,都会从 Facebook 团队的思考中找到启发。

In the JavaScript community, engineers share hundreds of thousands of pieces of code so we can avoid rewriting basic components, libraries, or frameworks of our own. Each piece of code may in turn depend on other pieces of code, and these dependencies are managed by package managers. The most popular JavaScript package manager is the npm client, which provides access to more than 300,000 packages in the npm registry. More than 5 million engineers use the npm registry, which sees up to 5 billion downloads every month.

在 JavaScript 社区里,开发者们共享出了成千上万的代码,这令我们不必自己从头开始编写最基础的组件、类库或框架。这些代码之间通常层层依赖,而这些依赖关系正是由包管理器来管理的。当前最流行的 JavaScript 包管理器是 npm 客户端,通过它,我们可以获取 npm 仓库中的 30 多万个包。超过 500 万名开发者在使用 npm 仓库,每个月产生 50 多亿次下载。

We've used the npm client successfully at Facebook for years, but as the size of our codebase and the number of engineers grew, we ran into problems with consistency, security, and performance. After trying to solve for each issue as it came up, we set out to build a new solution to help us manage our dependencies more reliably. The product of that work is called Yarn — a fast, reliable, and secure alternative npm client.

在 Facebook,我们已经成功地使用 npm 客户端多年了,但随着代码体积的不断增长和团队规模的不断壮大,我们在一致性、安全性和性能方面遇到了问题。我们曾尝试逐一解决这些问题,但最终,我们决定自己打造一套全新的解决方案,以一种更加可靠的方式来管理依赖。我们的工作成果叫作 Yarn——作为 npm 客户端的替代器,它更加快速、可靠、安全。

We're pleased to announce the open source release of Yarn, a collaboration with Exponent, Google, and Tilde. With Yarn, engineers still have access to the npm registry, but can install packages more quickly and manage dependencies consistently across machines or in secure offline environments. Yarn enables engineers to move faster and with confidence when using shared code so they can focus on what matters — building new products and features.

今天,我们很荣幸地宣布,Yarn 以开源的方式发布,它是由 Exponent、 Google、Tilde 与我们合作完成的。在使用 Yarn 时,开发者们还像以前一样从 npm 仓库那里获取资源,但安装速度更快,不同的机器的安装结果完全一致,甚至还可以在安全的离线环境中使用。Yarn 令开发者可以更加迅捷和从容地享受前人栽种的果实,进而集中精力打造自己的产品——这才是更加重要的事情。

The evolution of JavaScript package management at Facebook

JS 包管理方案在 Facebook 的演变

In the days before package managers, it was commonplace for JavaScript engineers to rely on a small number of dependencies stored directly in their projects or served by a CDN. The first major JavaScript package manager, npm, was built shortly after Node.js was introduced, and it quickly became one of the most popular package managers in the world. Thousands of new open source projects were created and engineers shared more code than ever before.

在包管理器出现之前,JavaScript 开发者平时所用的依赖并不多,这些依赖通常会直接存放在项目中,或通过 CDN 引入。npm 是第一个正式的 JavaScript 包管理器,在 Node.js 诞生后不久它就建立起来了,随后迅速成为全世界最流行的包管理器。从此,开源项目如雨后春笋般涌现,盛况空前。

Many of our projects at Facebook, like React, depend on code in the npm registry. However, as we scaled internally, we faced problems with consistency when installing dependencies across different machines and users, the amount of time it took to pull dependencies in, and had some security concerns with the way the npm client executes code from some of those dependencies automatically. We attempted to build solutions around these issues, but they often raised new issues themselves.

Facebook 的很多项目,比如 React,就依赖了 npm 仓库中的代码。但是,随着内部规模的扩张,我们遇到了很多问题:不同机器或不同人所得到的安装结果并不一致,安装依赖所花费的时间也无法忽视;由于 npm 客户端在安装依赖包时会自动执行其中的脚本,安全性也令我们顾虑重重。我们不断尝试针对这些问题建立解决方案,但这些解决方案往往又不断引发新的问题。

Attempts at scaling the npm client

关于理顺 npm 客户端的一系列尝试

Initially, following the prescribed best practices, we only checked in package.json

and asked engineers to manually run npm install

. This worked well enough for engineers, but broke down in our continuous integration environments, which need to be sandboxed and cut off from the internet for security and reliability reasons.

最开始,我们遵循官方推荐的最佳实践,只把 package.json

文件提交到代码库,然后要求工程师们手动运行 npm install

来安装依赖。这种方法对工程师来说运作良好,但在我们的持续集成(CI)环境下就不灵了,因为 CI 环境需要运行在沙箱中,基于安全性和可靠性的考虑,它需要切断与互联网的连接。

The next solution we implemented was to check all of node_modules

into the repository. While this worked, it made some simple operations quite difficult. For example, updating a minor version of babel

generated an 800,000-line commit that was difficult to land and triggered lint rules for invalid utf8 byte sequences, windows line endings, non png-crushed images, and more. Merging changes to node_modules

would often take engineers an entire day. Our source control team also pointed out that our checked-in node_modules

folder was responsible for a tremendous amount of metadata. The React Native package.json

currently lists just 68 dependencies, but after running npm install

the node_modules

directory contains 121,358 files.

后来我们改变了对策,把所有 node_modules

签入代码库。这种方法也运行了一段时间,但它也让一些原本简单的操作变得困难起来。举例来说,更新 Babel 的一个次版本号会产生一次 “80 万行” 级别的提交动作,这种情况很难处理,而且会触发一系列无意义的 lint 操作(诸如检查 UTF-8 字节序列、Windows 换行符、未优化的 PNG 图片等等)。把 node_modules

的变更合并进来往往要花费工程师一整天的时间。我们的版本控制团队也指出我们提交的 node_modules

目录直接导致元数据体积的飙升。比如 React Native 项目的 package.json

只列出了 68 个依赖,但在跑过了 npm install

之后, node_modules

目录下会产生 121,358 个文件。

We made one final attempt to scale the npm client to work with the number of engineers at Facebook and the amount of code that we need to install. We decided to zip the entire node_modules

folder and upload it to an internal CDN so that both engineers and our continuous integration systems could download and extract the files consistently. This enabled us to remove hundreds of thousands of files from source control, but made it so engineers needed internet access not just to pull new code, but also to build it.

为了让 npm 的这一套工作流适应 Facebook 的团队人数和代码量,我们动用了最后一招。我们决定把整个 node_modules

目录打成一个压缩包,上传到专用的 CDN 上,这样工程师和 CI 服务器都可以下载、解压并得到完全一致的结果了。这个方案一上,我们就可以从版本控制系统中清掉成千上万的文件了;但这同时也导致工程师不论是在拉取新代码时,还是在编写新代码时,都需要保持网络连接。

We also had to work around issues with npm's shrinkwrap

feature, which we used to lock down dependency versions. Shrinkwrap files aren't generated by default and will fall out of sync if engineers forget to generate them, so we wrote a tool to verify that the contents of the shrinkwrap file matches what's in node_modules

. These files are huge JSON blobs with unsorted keys, though, so changes to them would generate massive, difficult-to-review commits. To mitigate this, we needed to add an additional script to sort all the entries.

我们还不得不应付 npm 的 shrinkwrap 功能所带来的问题,因为我们使用 shrinkwrap 来锁定依赖版本。Shrinkwrap 文件并不是默认生成的,一旦工程师在提交代码前忘记生成这个文件,则这个文件跟代码就不同步了。为此我们还特意写了一个工具,用来校验 shrinkwrap 文件的内容跟 node_modules

的内容是否匹配。这个 shrinkwrap 文件是由一堆乱序字段所组成的巨型 JSON 数据,因此每次修改都会产生一次巨型的、无法 review 的提交。为了缓解这个问题,我们又需要写一个额外的脚本来把所有的字段排好序。

Finally, updating a single dependency with npm also updates many unrelated ones based on semantic versioning

rules. This makes every change much larger than anticipated, and having to do things like committing node_modules

or uploading it to a CDN made the process less than ideal for engineers.

这还没完呢。在 npm 中,更新一项单独的依赖其实还会按照 “语义化版本”(semantic versioning)的规则更新一堆无关的依赖。这使得每次代码变更都比预期的要多,而我们所能做的也就是上述这一系列的下策。

Building a new client


Rather than continue building infrastructure around the npm client, we decided to try looking at the problem more holistically. What if instead we attempted to build a new client that addressed the core issues we were experiencing? Sebastian McKenzie in our London office started hacking on this idea and we quickly became excited about its potential.

痛定思痛,我们决定不再围绕 npm 客户端来构建基础设施,而是从一个更高的高度来审视整件事情。如果我们针对当下的痛点来打造一款全新的客户端会怎么样?我们伦敦办事处的 Sebastian McKenzie 同学开始推进这个想法,很快我们就发现这条道路充满光明。

As we worked on this, we began speaking with engineers across the industry and found that they faced a similar set of problems and had attempted many of the same solutions, often focused on resolving a single issue at a time. It became obvious that by collaborating on the whole set of problems the community was facing, we could develop a solution that worked for everyone. With the help of engineers from Exponent, Google, and Tilde, we built out the Yarn client and tested and validated its performance on every major JS framework and for additional use cases outside of Facebook. Today, we're excited to share it with the community.

在推进这个计划的同时,我们也开始跟业内的工程师们广泛交谈。我们发现大家遇到的问题其实都差不多,也都尝试过差不多的解决方案,效果也差不多都是按下个葫芦浮起个瓢。接下来的事情就变得水到渠成了——我们集合社区的力量来解决共通的问题,产出一个适用于每个人的解决方案。由此我们收到了来自 Exponent、Google 和 Tilde 公司的工程师的帮助,共同开发出了 Yarn 客户端;为了确保它也适用于 Facebook 之外的使用场景,我们还在所有主流的 JS 框架身上验证了它的性能。今天,我们怀着激动的心情,将它推荐给整个社区。

Introducing Yarn

Yarn 介绍

Yarn is a new package manager that replaces the existing workflow for the npm client or other package managers while remaining compatible with the npm registry. It has the same feature set as existing workflows while operating faster, more securely, and more reliably.

Yarn 是一款新的包管理器,它将取代原有的基于 npm 客户端(或其它包管理器)的工作流,但同时又保留了与 npm 仓库的兼容性。它具备原有工作流的所有功能,但相比之下更加快速、安全、可靠。

The primary function of any package manager is to install some package — a piece of code that serves a particular purpose — from a global registry into an engineer's local environment. Each package may or may not depend on other packages. A typical project could have tens, hundreds, or even thousands of packages within its tree of dependencies.

所有包管理器的核心功能都是从一个通用仓库中获取包,然后安装到开发者的本地环境中。所谓 “包”,就是一套解决特定任务的代码。一个包可能依赖、也可能不依赖其它包。一个典型的项目可能会在它的依赖树中涉及数十、数百甚至数千个包。

These dependencies are versioned and installed based on semantic versioning (semver). Semver defines a versioning scheme that reflects the types of changes in each new version, whether a change breaks an API, adds a new feature, or fixes a bug. However, semver relies on package developers not making mistakes — breaking changes or new bugs may find their way into installed dependencies if the dependencies are not locked down.

这些依赖包都是有版本的,它们按照 “语义化版本”(semver)的规则被安装进来。Semver 定义了一套版本描述规范,用于表明每个版本的变更类型:是 API 行为出现了向后不兼容的破坏性变更、是增加了一个新功能、还是修复了一个 bug。但是,semver 是否奏效,取决于包的开发者是否遵守规则。在理想情况下,即使依赖包没有锁定版本,包的不同类型的变更版本都会在 semver 的约定下以不同的方式被正确地安装进来。



In the Node ecosystem, dependencies get placed within a node_modules

directory in your project. However, this file structure can differ from the actual dependency tree as duplicate dependencies are merged together. The npm client installs dependencies into the node_modules

directory non-deterministically. This means that based on the order dependencies are installed, the structure of a node_modules

directory could be different from one person to another. These differences can cause “works on my machine” bugs that take a long time to hunt down.

在 Node 的生态系统中,依赖会被安装到你的项目中的 node_modules

目录中。不过,其内部的文件结构和实际的依赖关系树并不完全对应,因为安装过程存在重复依赖的合并机制。npm 客户端在把依赖安装到 node_modules

目录时存在不确定性。这种 “不确定性” 是指,由于安装依赖的顺序不同,你得到的 node_modules

目录的内部结构可能跟别人不一样。这种差异可能会导致 “我电脑上是好的” 之类的 bug,而这类 bug 往往是极难定位的。

Yarn resolves these issues around versioning and non-determinism by using lockfiles and an install algorithm that is deterministic and reliable. These lockfiles lock the installed dependencies to a specific version, and ensure that every install results in the exact same file structure in node_modules

across all machines. The written lockfile uses a concise format with ordered keys to ensure that changes are minimal and review is simple.

Yarn 解决上述版本问题和不确性问题的方案是引入 lockfile(锁定文件),并启用了一套新的安装算法,以此达到一致、可靠的结果。这个 lockfile 会把所有已安装的依赖锁定在一个固定的版本上,确保每次安装所产生的 node_modules

目录的文件结构在不同机器上总是一致的。这个 lockfile 采用一种简明的格式来书写,其字段是有序的,以确保每次更新都是最小化的、易于 reivew 的。

The install process is broken down into three steps:


  1. Resolution:

    Yarn starts resolving dependencies by making requests to the registry and recursively looking up each dependency.

  2. Fetching:

    Next, Yarn looks in a global cache directory to see if the package needed has already been downloaded. If it hasn't, Yarn fetches the tarball for the package and places it in the global cache so it can work offline and won't need to download dependencies more than once. Dependencies can also be placed in source control as tarballs for full offline installs.

  3. Linking:

    Finally, Yarn links everything together by copying all the files needed from the global cache into the local node_modules


  1. 解析

    :Yarn 首先开始解析依赖关系。它向包仓库发出请求,并递归地查询各层依赖。

  2. 获取

    :接下来,Yarn 会在一个全局的缓存目录中查找当前所需的包是不是已经下载过了。如果还没有,Yarn 会把这个包的 tarball 拉下来,并把它存放在全局缓存中,这样它下次就可以离线安装了,无需重复下载。依赖包也可以以 tarball 的形式存放到版本控制系统中,以实现完全的离线安装。

  3. 链接

    :最后,Yarn 会把所需的所有文件从缓存中复制到本地的 node_modules


By breaking these steps down cleanly and having deterministic results, Yarn is able to parallelize operations, which maximizes resource utilization and makes the install process faster. On some Facebook projects, Yarn reduced the install process by an order of magnitude, from several minutes to just seconds. Yarn also uses a mutex to ensure that multiple running CLI instances don't collide and pollute each other.

由于我们把安装过程清晰地拆解开来,消灭了安装结果的不确定性,Yarn 天生具备并行操作的能力。这种并行能力可以最大化资源的利用率,提升安装速度。在 Facebook 的某些项目中,Yarn 可以带来一个数量级的性能提升,安装耗时从几分钟缩短到几秒。Yarn 内建互斥特性,以确保同时运行多个 CLI 实例也不会相互冲突、相互污染。

Throughout this entire process, Yarn imposes strict guarantees around package installation. You have control over which lifecycle scripts are executed for which packages. Package checksums are also stored in the lockfile to ensure that you get the same package every single time.

在整个安装过程中,Yarn 还提供了严格的安全保障。你可以精确控制某个包的某个生命周期脚本是否运行。包的校验信息(checksum)也会保存在 lockfile 中,确保你每次安装得到的都是同一个包。



In addition to making installs much faster and more reliable, Yarn has additional features to further simplify the dependency management workflow.

除了让包的安装变得更加快速和可靠以外,Yarn 还提供了如下特性,进一步简化了依赖管理的工作流。

  • Compatibility with both the npm and bower

    workflows and supports mixing registries.

  • Ability to restrict licenses of installed modules and a means for outputting license information.
  • Exposes a stable public JS API with logging abstracted for consumption via build tools.
  • Readable, minimal, pretty CLI output.

  • 同时兼容 npm 和 Bower 工作流,支持混用多种仓库类型。
  • 可以限制依赖包的授权类型,并且可以输出依赖包的授权信息。
  • 暴露一个稳定的 JS API,提供抽象化的日志信息以便与其它构建工具集成。
  • 提供可读的、最小化的、美观的 CLI 输出信息。

Yarn in production


At Facebook we're already using Yarn in production, and it's been working really well for us. It powers the dependency and package management for many of our JavaScript projects. With each migration we've enabled engineers to build offline and helped speed up their workflow. You can see how install times for Yarn and npm compare on React Native under different conditions, which you can find here


在 Facebook,我们已经将 Yarn 用于生产环境了,而且它跑得也确实不错。我们的很多 JavaScript 项目都是由它来完成依赖安装和包管理的。每当一个项目完成迁移后,工程师们都会获得离线工作的能力,进而加快工作流。我们提供了一个页面( https://yarnpkg.com/en/compare

),用来展示不同条件下 Yarn 和 npm 在安装耗时方面的对比。

[译] Yarn 官方介绍: 一款新的 JavaScript 包管理器

Getting started


The easiest way to get started is to run:


npm install -g yarn


The yarn

CLI replaces npm

in your development workflow, either with a matching command or a new, similar command:

新的 yarn

CLI 将会在你的开发工作流中替代 npm

。在各种场景下,Yarn 要么提供了一个对等的命令,要么提供了一个功能相似的新命令:

  • npm install


    With no arguments, the yarn

    command will read your package.json

    , fetch packages from the npm registry, and populate your node_modules

    folder. It is equivalent to running npm install


    在不加任何参数的情况下, yarn

    命令将会读取你的 package.json

    文件,从 npm 仓库拉取包,然后存放到你的 node_modules

    目录中。其效果等同于直接运行 npm install

  • npm install --save <name>

    yarn add <name>

    We removed the “invisible dependency” behavior of npm install <name>

    and split the command. Running yarn add <name>

    is equivalent to running npm install --save <name>


    我们去掉了 npm install <name>

    命令产生 “隐形依赖” 的行为,只保留了显式安装行为。运行 yarn add <name>

    将等同于运行 npm install --save <name>



Many of us came together to build Yarn to solve common problems, and we knew that we wanted Yarn to be a true community project that everyone can use. Yarn is nowavailable on GitHub and we're ready for the Node community to do what it does best: Use Yarn, share ideas, write documentation, support each other, and help build a great community to care for it. We believe that Yarn is already off to a great start, and it can be even better with your help.

我们这一群人走到一起构建了 Yarn,目的在于解决社区共通的问题;我们的愿景也很明确,希望 Yarn 成为一个真正的社区项目,人人均可从中受益。Yarn 已经在 GitHub 开源,我们也已经准备好迎接来自 Node 社区的帮助:开始使用、交换想法、编写文档、互相扶持,共同建立一个强大的社区来推动它的发展。我们相信 Yarn 已经迈出了坚实的第一步,而且你的参与会让它变得更好!

本文在 “CSS魔法” 微信公众号首发,扫码立即订阅:

[译] Yarn 官方介绍: 一款新的 JavaScript 包管理器

© Creative Commons BY-NC-ND 4.0   | 我要订阅

|   我要打赏