Yarn:一个新的JavaScript包管理器

本文翻译自: Yarn: A new package manager for JavaScript可能需要翻墙
本博客英文原文链接:Yarn - A new package manager for JavaScript墙内可以查看此文

yarn

在Javascript社区中,工程师们共享成千上万个代码片段,以便于我们可以避免重写自己的基本组件、库或者框架。每个代码片段都可以依赖于其他的代码片段,这些依赖关系由包管理器进行统一管理。最流行的JavaScript包管理器是npm,在npm的注册表中,有超过30万个包可供我们访问与使用。超过500万的工程师使用npm注册表,每月产生50亿次的下载量。

npmnode package manager的缩写。 ——译者注

我们在FaceBook上成功地使用了npm客户端多年,但随着我们的代码库的大小和工程师数量的增长,我们遭遇到了一致性、安全性、和性能方面的问题。在尝试解决每个问题后,我们开始构建一个新的方案来帮助我们更加可靠地管理我们的依赖。这个产品被称之为Yarn——一个快速、可靠、安全的npm客户端替代品。

我们很高兴地宣告与Exponent、Google以及Tilde一起合作研发的Yarn的开源发布。使用Yarn,工程师依然可以访问npm注册表,但是在使用安装包时会更加快速,并且可以在各个机器或者安全的离线环境中一致地管理依赖。Yarn使工程师能快速活动,同时在使用分享代码时更加自信,以便于他们可以专注于重要的事情——构建新的产品和特性。

ExponentGoogleTilde均是美国知名科技公司。 ——译者注

Facebook中JavaScript包管理器的演进

在包管理器之前的年代, JavaScript工程师通常将少量的依赖直接存储在项目中或者由CND提供。第一个主要的JavaScript包管理器npm是在Node.js推出之后不就建立的,它很快地就成为了世界上最受欢迎的包管理器之一。上千个新的开源项目被创建,工程师也共享了比之前更多的代码。

在Facebook,我们的许多项目,如React,依赖于npm注册表中的代码。但是,在我们的内测中,在不同机器和用户之间安装依赖时,我们面临着一致性的问题,拉取依赖所消耗的时间,甚至npm客户端从某些依赖自动执行造成一些安全问题。我们试图针对这些问题制定解决方案,但它们常常又会引发新的问题。

尝试缩放npm客户端

最初,按照既定的最佳实践,我们只检查package.json并要求工程师手动执行npm install。对于工程师来说,这种方式很好,但是在我们的可持续集成环境中就出故障了,需要放在沙箱中并且切断网络以保证安全性和可靠性。

接下来的实现方案是,检查所有的node_modules到仓库中。虽然这个可以工作,但是让一些简单的操作变得困难。举个例子,更新一个产生了80万行提交的babel的主要版本就变得异常困难,并且会引发无效的UTF-8字节序列、Windows行尾(CRCRLF的区别,译者注)、非PNG损坏图像等等。合并node_modules的更改通常会花费工程师一整天的时间。我们的源代码控制团队还指出,我们签入的node_modules文件夹,需要负责承载大量的元数据。React Nativepackage.json中目前只包含了68个依赖,但是在执行完npm install后,node_modules目录下将包含121358个文件。

我们做了最后一次尝试,缩放整个npm客户端,处理Facebook的工程师数量和我们需要安装的代码量。我们决定对整个node_modules文件夹进行压缩并上传至一个内部CDN,这样可以使工程师和我们的可持续集成系统都可以下载和解压这些文件,以保证一致性。这使得我们从代码控制中删除了成千上万个文件,但这样工程师需要互联网访问,不仅仅只是拉取新的代码,还需要构建它。

我们还必须解决npmshrinkwrap特性,用来锁定依赖版本。默认情况下,Shrinkwrap文件不会自动生成。如果工程师忘记生成它们,将会出现不一致的情形。针对这个,我们写了一个工具用来验证shrinkwrap文件的内容是否匹配node_modules中的。这些文件是大体积JSON blob并且键值是无序的,这导致了对它们的更改将会产生大量的、难以审查的提交。为了缓和这样的情形,我们需要添加一个额外的脚本来对所有的条目进行排序。

最终,使用npm更新一个单一的依赖同样会更新许多基于版本语义化 规则的不相关的依赖。这使得每一个更改都比预期的要大,并且不得不做一些事情比如提交node_modules或者将其上传至CDN,使其过程比工程师想象的少一些。

构建一个新的客户端

我们并非打算在npm客户端周围继续构建一个基础设施,而是决定尝试更加全面地查看问题。如果我们试图创建一个解决我们遇到的核心问题的新的客户端,结果会怎么样呢?我们在伦敦办事处的Sebastian McKenzie开始关注到的这个想法,让我们很快对其潜力感到兴奋。

在我们开展工作时,我们开始与整个行业的工程师交谈,发现他们也面临着同样的问题,也尝试过很多相同的解决方案,通常专注于一次解决一个问题。很明显,通过协作,解决社区面临的一系列问题,我们可以开发出一套适用于每个人的解决方案。在Exponent、Google以及Tilde的工程师的帮助之下,我们创建了Yarn客户端,并测试和验证了它在每个主要的JS框架以及Facebook外的其他用例方面的性能。今天,我们很高兴能在社区进行分享。

Yarn简介

Yarn是一个新的包管理器,可以替换现有的npm客户端或者其他包管理器的工作流,同时保持与npm注册表的兼容。它与现有的工作流有着同样的特性,同时操作更快速、更安全、更可靠。

任何包管理器的主要功能是从一个全局注册表安装一些软件包——一个有些特定目的的代码片段——到工程师的本地环境。每个包可以也可以不依赖于其他包。一个典型的项目中,其依赖可能包含有数十、数百甚至数千个包。

这些依赖关系是版本化的,根据版本语义化,即semver,进行安装。Semver定义了一个版本规划用来映射每个新版本中的更改类型,如改动API、添加新特性、修复bug。然而,semver依赖于包开发人员不犯错误——如果依赖未被锁定,在已安装的依赖中可能会找到破坏性变化或者新的bug。

架构

在Node的生态系统中,依赖会放置在你的项目中node_modules的一个目录下。但是,此文件结构会与实际的依赖关系树不同,因为重复的依赖关系会合并在一起。npm客户端安装依赖到node_modules目录下时会具有不确定性。这意味着,基于顺序的依赖被安装,一个人的node_modules目录的结构可能会与另一个人的不一样。这种不同导致了要花很长时间才能发现的“在我的机器上可以工作”的问题。

Yarn通过锁定文件和一个具有确定性和可靠性的安装算法来解决版本控制和非确定性方面的问题。这些锁定文件会将已安装的依赖锁定到一个指定的版本,并确保在所有机器上的每一次安装结果中node_modules的文件结构都会是相同的。写好的锁定文件使用了带有序键值的简明格式以确保更改是最小的,审查起来也很简单。

安装过程分为以下三个步骤:

  1. 决议:Yarn开始通过请求注册和递归查找每一个依赖,决定要安装的依赖。
  2. 获取:接着,Yarn在全部缓存目录中查看所需要的包是否已经下载。如果没有,Yarn获取包的tarball并放置的全局缓存中,这样它可以脱机工作并且不用多次下载依赖。依赖也可以放置在代码控制中作为完整的离线安装tarball
  3. 连接:最后,Yarn通过从全局缓存中复制所有需要的文件到本地的node_modules目录来串联所有东西。

tarball是压缩包的意思。 ——译者注

通过清晰的分解步骤和具有确定定的结果,Yarn可以进行并行操作,从而最大限度地利用资源,并使安装过程更快。在一些Facebook的项目中,Yarn将安装过程减少了一个量级,从几分钟锐减到几秒钟。Yarn还可以使用互斥来保证多个在运行的CLI实例之间不会互相冲突和污染。

在整个过程中,Yarn对包的安装强制进行了严格的保证。你可以控制某个包的某个生命周期脚本。包的校验也可以存储在锁定文件中,以确保每次获取的包是一样的。

特性

除了使安装更快、更可靠之外,Yarn还有进一步简化依赖管理工作流的附加功能。

  • 兼容npmbower的工作流,并支持混合注册模式。
  • 能够限制已安装模块的许可证,并且有输出许可证信息的方法。
  • 暴露一个稳定的公共的JS API,通过构建工具可以进行消费日志文件的提取。
  • 可读、最小化、美观的CLI输出。

Yarn的使用

在Facebook,我们已经在生产中使用Yarn,它为我们一直都工作地非常好。它为我们的许多JavaScript项目提供了依赖和包管理。每次迁移,我们能够让工程师离线构建并帮助他们加快工作流。在这里,你可以看到在不同的条件下YarnnpmReact Native的安装情况的比较。

快速开始

最简单的入门方法是运行:

1
2
npm install -g yarn
yarn

在你的开发工作流中,不论是一个匹配的命令还是一个新的,将Yarn的CLI替换掉npm的即可。类似的命令:

  • npm installyarn

    不带任何参数,则yarn命令将会读取你的package.json,从npm注册表中获取包,并填充至你的node_modules目录下。这等同于运行npm install

  • npm install --save <name>yarn add <name>

    我们删除了npm install <name>的“隐形的依赖”的行为并拆分命令。运行yarn add <name>npm install --save <name>效果等同。

未来

我们许多人共同创建Yarn来解决常见的问题,我们希望Yarn能够成为一个真正的社区项目,每个人都可以使用。Yarn现在架设在Github上,我们已经准备好了Node社区来让它变得更好:使用Yarn、分享观点、写文档、互相支持,并建立一个伟大的社区来维护它。我们相信Yarn已经有了一个伟大的开始,有你的帮助它会变得更好。


全文完

本文翻译自: Yarn: A new package manager for JavaScript
翻译者:xovel
翻译时间:2016年10月13日