facebook yarn -- 一个可能取代npm的新型包管理器

原文链接 : Yarn: A new package manager for JavaScript
原文作者 : SEBASTIAN MCKENZIECHRISTOPH POJERJAMES KYLE
译文出自 : 掘金翻译计划
译者 : 达仔
校对者: 根号三

JavaScript 包管理方式在 Facebook 的演变
在包管理工具出现之前,JavaScript 工程师们通常依赖的项目并不多,因此会把依赖直接存储在工程目录或上传到 CDN 上。在 Node.js 出现后不久,第一个主流的 JavaScript 包管理工具 npm
被引入进来,并很快成为了最受欢迎的包管理工具之一。从此,新的开源项目不断涌现,工程师们比起以前更加乐于分享代码了。
在 Facebook 中,我们有很多项目都要依赖 npm
仓库上的代码,比如 React。但随着内部规模的扩大,我们面临着以下挑战:在跨平台与跨用户之间安装依赖时的代码一致性问题、在安装依赖时花费太长时间、以及 npm
客户端自动执行某些依赖库的代码所导致的安全性问题。我们尝试过寻找这些问题的解决方案,但在这个过程中通常又会引起一些新的问题。
尝试修改 npm 客户端
在开始阶段,我们遵循了最佳实践,在代码仓库中只跟踪了 package.json
文件的变化,并要求工程师手动运行 npm install
命令安装依赖。这种模式在开发人员的电脑上没有问题,但在持续集成环境中遇到了困难,因为出于安全与可靠性的考虑,持续集成环境需要进行沙箱隔离,不能进行联网,因此也无法安装依赖。
接下来,我们尝试在代码仓库中跟踪整个 node_modules
目录的文件变化。虽然这种方式有效,却使得一些简单操作变得复杂化了。比如,对 babel 更新一个次要版本号时,会产生多达 800,000 行的提交记录,此外由于 lint 规则的存在,引起无效的 utf-8 字节序列、windows 换行符、非 png 压缩图片等问题时,将会导致工程师经常需要花费一整天的时间合并 node_modules
目录的文件。而我们负责源码控制的团队也指出,跟踪 node_modules
目录会引入过多的元数据。比如 React Native 的package.json
文件目前只列出了68项依赖,但在运行 npm install
后,node_modules
目录整整包含了 121,358 个文件。
最后,为了有效组织 Facebook 逐渐增长的工程师人数以及管理需要安装的代码量,我们尝试修改npm
客户端。我们决定压缩整个 node_modules
目录,并上传到内部 CDN,然后我们的工程师与持续集成系统都能从 CDN 上下载并解压文件,从而保证了代码一致性。这样我们就可以从源码控制系统中删除数以万计的文件了,但不足之处是工程师现在不仅在拉代码时需要联网了,构建也同样需要联网。
我们还试图为 npm
shrinkwrap 功能寻求优化方案,这个工具是用来锁定依赖版本号的。但Shrinkwrap
功能的文件默认不会生成,如果开发者忘记了生成这一步骤,文件就不会被同步更新,因此我们编写了一个工具,以确定 Shrinkwrap
的文件内容和 node_modules
目录中的文件相符。这些文件由大量的 JSON 块组成,并且键名是无序的,因此每次更改通常会导致 Shrinkwrap
文件的内容大幅变化,难以进行代码审查。为减缓这一问题,我们还需要借助一个额外的脚本,对所有条目进行排序。
最后,通过 npm
升级单个依赖包时,基于 语义化版本号 规则,npm
通常会连同其他无关依赖一起更新。这使得每次更新都会比预期产生更多的变化,工程师们认为这样把 node_modules
提交上传到 CDN 的过程,难以达到预期的效果。
构建新客户端
与其围绕 npm
客户端继续构建基础设施,不如从整体上再次回顾这些问题。伦敦办公室的 Sebastian McKenzie 提出,如果我们建立一个新客户端工具以代替 npm
客户端,从而解决我们的核心问题呢?这一构思很快得到了我们的认同,团队对于这个主意也感到非常兴奋。
在开发过程中,我们与业界的工程师们进行了交流讨论,发现他们也面临着类似的问题,也尝试过许多类似的解决方案,通常只能把这些问题逐一解决。很明显,有必要把整个 JavaScript 社区正在面临的问题集合起来,然后我们就可以开发一个主流的解决方案了。在此感谢 Exponent、 Google 与 Tilde 的工程师们的协助,我们共同建立了 Yarn
客户端,并在每一个主流 JS 框架以及 Facebook 外的使用场景中测试验证了 Yarn 的性能。今天(2016-10-11),我们很荣幸把这个工具开源分享到社区中。
介绍 Yarn
Yarn
是一个新的包管理器,用于替代现有的 npm
客户端或者其他兼容 npm
仓库的包管理工具。Yarn
保留了现有工作流的特性,优点是更快、更安全、更可靠。
任何包管理器的主要功能都是安装某些软件包,软件包即用于特定功能的某段代码,通常是从一个全局的仓库安装到工程师的本地环境。每个软件包可以依赖于其他包,也可以不依赖。一个典型的项目结构的依赖树通常会包含数十个、数百个甚至上千个软件包。
这些依赖包通常是带版本号的,通过语义化版本控制(semver)安装。Semver 定义的版本号反映了每个新版本更改的类型,到底是进行了不兼容的API改动(MAJOR),还是添加了向后兼容的新特性(MINOR),还是进行了向后兼容的 bug 修复(PATCH)。然而,semver 依赖于软件包的开发者不能犯错误——如果依赖关系没有加锁,可能会引入一些破坏性更改或者产生新的 bug。
结构
在 Node 生态系统中,依赖通常安装在项目的 node_modules
文件夹中。然而,这个文件的结构和实际依赖树可能有所区别,因为重复的依赖可以合并到一起。npm
客户端把依赖安装到 node_modules
目录的过程具有不确定性。这意味着当依赖的安装顺序不同时,node_modules
目录的结构可能会发生变化。这种差异可能会导致类似“我的机子上可以运行,别的机子不行”的情况,并且通常要花费大量时间定位与解决。
Yarn
通过 lockfiles 文件以及一个确定性的、可靠的安装算法,解决了版本问题和 npm
的不确定性问题。Lockfile 文件把安装的软件包版本锁定在某个特定版本,并保证 node_modules
目录在所有机器上的安装结果都是相同的。Lockfile 还使用简洁的有序键名的格式,保证了每次的文件变化最小化,进行代码审查也更为简单。
安装过程分为以下三个步骤:
处理: Yarn
通过向代码仓库发送请求,并递归查找每个依赖项,从而解决依赖关系。
抓取: 接下来,Yarn
会查找全局的缓存目录,检查所需的软件包是否已被下载。如果没有,Yarn 会抓取对应的压缩包,并放置在全局的缓存目录中,因此 Yarn
支持离线安装,同一个安装包不需要下载多次。依赖也可以通过 tarball 的压缩形式放置在源码控制系统中,以支持完整的离线安装。
生成: 最后,Yarn
从全局缓存中把需要用到的所有文件复制到本地的 node_modules
目录中。

通过清晰地细分这些步骤,以及确定性的算法支持,使得 Yarn
支持并行操作,从而最大化地利用资源,并加速安装进程。在一些 Facebook 的项目上,Yarn
甚至可以把安装过程降低一个数量级,从几分钟到只需几秒钟。Yarn
还使用了互斥锁,以确保多个 CLI 实例同时运行时不会互相冲突与影响。
纵观整个过程,Yarn
对于软件包安装加上了严格的限制。你可以对哪个生命周期脚本作用于哪个软件包进行控制。软件包的 checksum 也会存储在 lockfile 中,以确保每一次安装都可以得到同一个包。
特性
Yarn
除了让安装过程变得更快与更可靠,还添加了一些额外的特性,从而进一步简化依赖管理的工作流。
同时兼容 npm
bower 工作流,并支持两种软件仓库混合使用
可以限制已安装模块的协议,并提供方法输出协议信息
提供一套稳定的公有 JS API,用于记录构建工具的输出信息
可读、最小化、美观的 CLI 输出信息

Yarn 用于生产环境
我们已经在 Facebook 中把 Yarn
用于生产环境,并且效果非常理想。Yarn
有效地管理了许多 JavaScript 项目的包依赖关系。在每次迁移时,构建都可以离线进行,因此加速了工作流程。我们基于 React Native 在不同条件下进行安装时间测试,比较了 Yarn
与 npm
的性能,具体参见这里


起步
最简单的起步方法是:
npm install -g yarnpkgyarn

yarn
CLI 代替了原有开发工作流中 npm
CLI 的作用,用法可能是单纯的替代,也可能是一个新的、相似的命令:
npm install
→ yarn

不需要带参数,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
的未来将会更加美好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容