tomli 2019-06-20
在JavaScript社区,工程师们分享了成百上千的代码段,我们不用自己从头编写基础组件、类库或者框架。反过来,每段代码又或许依赖于其它的代码段,而这些依赖就是通过 package managers(包管理器)
来管理的。最流行的JavaScript包管理器就是 npm客户端
了,它可以访问在npm注册的 300,000 多个安装包。超出500万的工程师使用npm注册,每个月的下载量高达 50 亿。
我们成功地在Facebook用了几年的npm客户端。但随着代码库规模的扩大以及开发者人数的增加,我们碰到了连贯性
,安全性
和 性能
方面的问题。在迎难而上解决一个又一个的问题后,我们着手构建一个新的方案,来帮助我们更可靠地管理我们的依赖。执行这项工作的产品名为 Yarn
-- 它是一个快速、可靠、安全的、可替换的npm客户端。
我们很高兴地宣布与Exponent、 Google 和 Tilde 合作的 Yarn
开源版本。有了 Yarn之后, 软件工程师们仍然可以访问npm库,而且可以更快地安装npm包,并且跨机器或者在安全的离线环境下一致性地管理依赖。Yarn 使得工程师可以更加快速开发,并且信心百倍地使用分享的代码库,从而专注于更重要的事情 -- 构建新的产品和特性。
在还没包管理器的时候,JS工程师常常依赖于存储在他们项目中或者放在CDN上面的少量代码段。第一个主要的JS包管理器 npm
在Node.js被引用后不久就搭建起来了,并且迅速成为世界上最受欢迎的包管理器之一。上千个新的开源项目被建立了,工程师们也比以往分享了更多的代码。
很多Facebook的项目(如React),都是依赖于npm包。然而,随着内部规模的扩大,我们都遇到了这样的问题:当跨机器或用户安装依赖时,拉取依赖包消耗的时间较长,也考虑到安全性的问题,因为npm客户端会自动执行这些依赖包里面的代码。我们尝试去解决这些问题,却发现这些问题还不断地衍生出新的问题。
起初,按照指定的最佳实践,我们只是检入 package.json
并且要求开发者手动地执行npm install
。这足以满足软件工程师的开发需求,但在我们持续集成的环境中就崩溃了,因为处于安全性和可靠性的考虑,这种环境需要沙箱化,并且切断网络服务。
我们的第二个方案是检入所有进入代码库的 node_modules
。这种做法虽然奏效,却使得一些原本简单的操作变得相当困难。举个例子,更新一个小版本 babel 就会生成一个80万行的commit,这对于无效的UTF8字节串、窗口的行尾退出符号,非 png-crushed 的图片等都很难处理。工程师们经常都要花上一整天的时间,才能把改动的代码合并到 node_modules
中。我们掌控源代码的团队也指出:检查 node_modules
文件是对大量元数据负责任的做法。React Native 的 package.json
文件目前只是列出了68个依赖,但执行 npm install
之后,node_modules
目录下就生成了121,358个文件。
我们做了最后一个尝试来规划npm客户端的大小,使之可以和开发者的数量以及我们所需的代码量来协调工作。我们觉得压缩整个 node_modules
文件夹,并且把它上传到内部的CDN,这样开发者和我们持续的集成系统都可以下载,并一致地提取文件。这使得我们可以从原文件中删除成千上百个文件。但是,这样做软件开发者不仅是拉取还是构建新代码,都需要网络服务。
我们也必须应对 npm shrinkwrap 功能所带来的问题,因为我们通过 shrinkwrap 来锁定版本。npm-shrinkwrap.json
不是默认生成的,如果开发者忘记生成,就会导致不同步的问题。因此我们写了一个工具,来验证安装包里的文件与 node_modules
中的哪些文件相匹配。npm-shrinkwrap.json
是由大量无序字段组成的 JSON blobs
,所以改变它们就会生成庞大的、无法 review 的commits
。为了缓和这个问题,我们需要额外添加一个脚本来区分所有的条目。
基于 semantic versioning(语义法版本控制) 原则,用npm更新一个简单的依赖, 也会相应更新许多不相关的依赖。这样使得每次改变的代码量大大增加, 而且必须重复提交 node_modules
或者上传到CDN这样的事情,这个过程对开发者来说是不理想的。
我们决定尝试整体地去看待这个问题,而不是在npm客户端继续修复。假如我们尝试搭建了一个新的客户端来处理遇到的这些核心问题又会怎样呢?我们伦敦工作室的 Sebastian McKenzie 已经开始hack这个想法,我们也为它的前景而兴奋。
随着这项工作的展开,我们开始和不同领域的工程师交流,并发现他们也遇到一系列类似的问题,也尝试了很多相同的解决方案,经常都是专注于解决眼下的问题。显然,通过结合大家所遇到的问题,我们可以开发一套共用的方案。在来自Exponent、Google 和 Tilde的工程师的帮助下,我们搭建了Yarn客户端,并在每个主要的JS框架上以及Facebook之外的场景测试和验证了它的性能。今天,我们带着激动的心情来和大家一起分享它。
Yarn 是一款新的包管理器,在取代npm客户端和其他包管理器现有工作流的同时,又保留了对npm代理的兼容性。它拥有与现有的工作流相同的特性,只是操作起来更快、更安全、更可靠。
任何包管理器的主要功能都是去安装一些依赖模块 -- 一段服务于特定的目的代码 -- 从一个全局的注册到开发者的本地环境。包与包之间可能相互依赖,也可能是自己独立的。一个典型的项目的依赖包可能会有几十个、几百个或者是上千个。
这些依赖都是版本化的,而且都是基于 语义版本控制
安装的。语义版本定义了一套版本的方案,无论是改了一个API,添加了一个新特性,还是修复了一个bug,都会在每个新版本上反映出来。然而,语义版本需要包开发者确保不出错,因为在依赖没有锁定的情况下,新的bug或漏洞都会被注入到依赖包里面。
在Node环境下,依赖都放在你项目的 node_modules
文件夹下。然而,这些文件结构或许和实际的依赖树不一致,因为重复的依赖会被合并到一起。npm客户端在安装依赖到 node_modules
目录时顺序不是固定的。这意味着随着安装顺序的不同,不同开发者 node_modules
下的目录结构会存在差异。这种差异有可能引发“在我的电脑上是好的”bug,找出这种bug通常是比较耗时的。
Yarn 通过 yarn.lock
和一个可靠、确定的算法解决了上面提及的版本控制和安装顺序这些问题。这些锁定文件为安装的依赖添加一个特定的版本号,并且确保在不同机器上安装时 node_modules
目录结构相同。lockfile
使用简明的书写格式和有序的 key
来确保改动尽量小,review起来更加简单。
安装过程可拆分为三个步骤:
识别: 开始通过发送请求并依次查找每个依赖来识别其中的依赖关系。
获取: 接下来,Yarn 查看全局缓存来确定所需的安装包是否已经下载。如果没有,Yarn 就抓取压缩包并把它放在全局的缓存中,既可以做到离线工作,也可以确保以后需要时无须再次下载。依赖也可以像压缩包一样放在源码中,以便离线安装。
连接: 最后,Yarn 从全局的缓存中复制所需的文件放置到本地的 node_modules
目录下连接起来。
通过明确地拆分这些步骤,得到确定的结果来实现并行操作,这样资源利用得到最大化,安装速度也大大提升。在Facebook 的一些项目中,Yarn 把安装速度提升了一个梯度,从几分钟到短短几秒。Yarn 也使用互斥来确保多个命令行安装时不会冲突或者互相污染。
Yarn 对于整个压缩包的安装过程都有严格的保证。你可以控制不同生命周期的脚本来执行对应的安装包。压缩包 校验和
也被存储在 lockfile
以确保你每次都能拿到同样的压缩包。
除了让安装过程更快更可靠,Yarn 还有额外的特性来更好地简化依赖管理的工作流。
兼容 npm 和 bower 工作流,并且支持混合注册。
能够限制已安装模块的证书以及输出证书信息。
暴露一个稳定公开的JS API,通过构建工具提供抽象的日志记录。
可读、最小化、良好的命令行输出。
我们已经在 Facebook 的生产环境下使用 Yarn 了,也运行得相当好。它颇有力度地为我们的很多 JavaScript 项目管理了依赖和压缩包。随着每次升级,工程师们已经能够离线搭建,并且加速他们的工作流。你可以在这里 查看不同环境下 React Native 分别用 Yarn 和 npm 的安装所消耗的时间。
<figure></figure>
最简单的使用方式就是运行命令:
npm install -g yarnpkg yarn
在你的开发流程中 Yarn
CLI 取代了 npm
, 要么用一个匹配的命令,要么使用一个新的、简单的命令:
npm install
→ Yarn
不带参数的情况下,Yarn
命令会读取你的 package.json
文件,从 npm 库中抓取所需的压缩包然后生成 node_modules
文件夹。这跟你运行 npm install
是一样的。
我们移除了 npm install
<name> 的隐形依赖并拆分了命令。执行
yarn 后面加上
<name> 与
npm install --save <name>
的是一样的。
我们许多人聚集到一起,共同搭建了 Yarn 来解决常见的问题。我们希望 Yarn 可以真正成为一个开源项目,方便每个人使用。Yarn 现在已经放到 github 上了。我们也欢迎 Node 社区的伙伴们使用 Yarn,分享idea,撰写文档,我们互相支持,让更多人来关注它、使用它!我们相信 Yarn 已经有个很好的开端了,未来在大家的帮助下,一定会变得更好!