avatar

都2024年了,别再用 Yarn v1 啦

/ 最后更新于

前言

最近在 Cloudflare Page 上部署一个 Next.js 项目时收获了满屏幕的红色日志,其中的一个错误是“检测到 Yarn v1 配置文件“。 原来此时的 CloudFlare Page 默认以 Yarn3 和 Node.js 18 作为 builder runtime。老实说,我从来没有在意过 Yarn 的版本号,它一直都工作得很好,不过日志都这么说了,让我们看看新版的金坷垃好处都有啥。

误区

在此之前,我一直以为使用了这行魔法命令之后就能吃上最新版的 Yarn 了,而事实根本不是这样。

corepack enable

先来看看历史,Node.js 从大概版本 14 开始加入了实验性的corepack用于管理诸如 Yarnpnpm 这样的第三方包管理器。得益于此,我们终于可以不再手动安装 Yarn 了。

# 眼熟这些命令吗?
npm install -g yarn
npm i -g corepack

而启用 corepack 后默认的 Yarn 版本其实是 classic(v1) 的最新版,本文成稿时为v1.22.22,这个版本并没有像我想的那样指向 Yarn 最新的大版本,官方给出的一个解释是人们有很多代码都还在使用 v1 并依赖其 API 行为,因此不能贸然指向一个与原来可能不兼容的 Yarn 版本。

而真正要切换到 Yarn 的最新版,必须使用这个命令:

#现在你确实地在使用最新版了!
yarn set version stable

综上所述,我对 corepack 和 Yarn 有两个认知上的误区:

  • Yarn 不是与 Node.js 一起捆绑发布的
  • corepack 默认启用的不是最新的大版本 Yarn

那新版好处都有啥

看了一圈文档,新版最大的变化就是新引入的Plug’n’Play - PnP,其相关特性如下:

  • 通过重用包来改善依赖体积问题和 IO 问题
  • 限制对幽灵依赖的访问来帮助开发者提前发现问题
  • 更优秀的错误提示

多年来,Nodejs 开发者们对 node_modules 的体积和性能都颇有微词,这也催生了像 pnpm 这样以 “快速、节约磁盘空间” 为口号的包管理器的诞生,而现在 Yarn 也对这个诟病已久的问题交出了自己的答卷。

🍽️ 吃螃蟹

在我们开始前,请确认以下事项:

  1. Node.js > 18 (因为 Yarn v4 开始要 18 以上了)

  2. 如果你在使用 React Native 或者 flow 这种库,目前还不能使用 PnP,但是可以升级 Yarn 版本。

第一步确保 corepack 已启用,不确定就敲一个 yarn -v试试。

corepack enable

初始化项目

对于全新的项目来说启用 Yarn 新版再简单不过,请在你的新项目文件夹中使用:

yarn init -2

接下来,Yarn 会自动初始化 Git 仓库和设置 .gitignore.editorconfig 等等配置。

在安装了依赖后,眼尖的朋友此时应该已经在根目录发现了一个陌生的文件: .pnp.cjs,并且注意到它是被 Git 所忽略的,而熟悉的node_modules不见踪影,这就是我们的 PnP。

升级项目

如果你已经有了一个包含 Yarn classic(v1) 的项目,请使用以下命令:

yarn set version stable
yarn install

你应该看到类似的输出:

➤ YN0000: Done in 0s 9ms
❯ yarn install
➤ YN0087: Migrated your project to the latest Yarn version 🚀

➤ YN0000: · Yarn 4.2.2
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + @astrojs/check@npm:0.7.0, @astrojs/react@npm:3.3.4, @astrojs/rss@npm:4.0.6, @astrojs/tailwind@npm:5.1.0, @types/react-dom@npm:18.3.0, @types/react@npm:18.3.2, and 622 more.
➤ YN0000: └ Completed in 1s 912ms
➤ YN0000: ┌ Fetch step
➤ YN0013: │ 14 packages were added to the project (+ 24.61 MiB).
➤ YN0000: └ Completed in 1s 3ms
➤ YN0000: ┌ Link step
➤ YN0007: │ esbuild@npm:0.21.3 must be built because it never has been before or the last one failed
➤ YN0007: │ sharp@npm:0.33.4 must be built because it never has been before or the last one failed
➤ YN0007: │ esbuild@npm:0.20.2 must be built because it never has been before or the last one failed
➤ YN0000: └ Completed in 5s 117ms
➤ YN0000: · Done in 8s 84ms

如果一切顺利,你会发现项目根目录下还自动创建了一个.yarnrc.yml文件,里面只有一行配置:

nodeLinker: node-modules

此外,还有一个陌生的.yarn文件夹,你也许会疑惑是否应该把这些文件添加进版本管理,不要担心,请根据官方文档的说明来手动更新 .gitignore:

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

到此为止,你的项目已经成功升级 Yarn 到最新版,只是对于从 v1 迁移而来的项目,PnP 不会默认启用,免得搞炸你的项目,所以你还有熟悉的 node_modules 陪着你。

追求刺激贯彻到底

如果你真的很想在旧的项目上试试 PnP,请确保到上一步为止你的修改已经提交,以便于随时回滚。接下来我们编辑 .yarnrc.yml文件:

# 从 node-module 改为 pnp
nodeLinker: pnp

或者直接删除这项配置,因为 PnP 是 Yarn v2 及之后的默认 NodeLinker 行为。

然后运行 yarn install,接下来,你会看到 node_modules 目录被删除,取而代之的是 .pnp.cjs.pnp.loader.mjs.yarn目录。

你可能会对它们有疑问:

  • .pnp.loader.mjs是 Yarn 实验性的 ESM loader
  • .yarn/unplugged 下可能会存在一些 Yarn 自动弹出的包

到这里,PnP 的启用已经完成,接下来你可以开始试着启动项目,看看是否一切正常,如果不幸还是发生了,请开始跟着错误日志排除错误,或者放弃 PnP 回到 node_modules 温暖而熟悉的怀抱,选择权在你。

不一样的地方

这个章节主要描述使用了 PnP 之后的一些常见行为变化,如果你不使用 PnP 的话就可以跳过,对于完整的改动请参阅官方文档

Yarn global 已被替换为 Yarn dlx

根据官方文档Renamed的描述,yarn global 已经被替换为了yarn dlx,用来执行那些只运行一次的代码。看起来这就是 yarn 版的 npx

使用 yarn run 而不是 node_modules/.bin

鉴于已经没有 node_modules 目录了,这个命令自然也不再有效,请改用 yarn run

使用 yarn node 而不是直接 node 来运行你的脚本

简单来说以前会像这样执行脚本:

node main.js

但是有了 PnP 之后,查找依赖的方式发生了变化,Yarn 需要先把依赖描述文件注入到运行时里。如果你使用 yarn script,这个步骤是自动的,否则请改用:

yarn node main.js

不是很靠谱的体积对比

以我手里的一个 Astro 项目为例:

使用 PnP 前,node_modules 目录的体积为 294MB 左右。

使用 PnP 后,PnP 文件体积为 694KB,由于 .yarn 目录中还有自动 unplugged 的包,因此也将其 41MB 的体积计算在内,再算上 loader 的体积 70KB,这样的表现可以说非常出色!

写在最后

Yarn v2 的首个正式版本早在 2020 年就已发布,第一次写下这篇文章的时候是 2023 年,那时 Yarn 的最新版是 v3,而第二次修订本文于 2024 年,Yarn 也已迎来了自己的 Yarn v4。由于 Yarn Classic(v1) 之后的版本都不再通过 npm 分发,因此没有一个很好的渠道去统计到底有多少开发者升级到了新的 Yarn,但我个人从日常开发中观测到的一些 cli 脚手架创建出来的 Yarn 还是清一色的 Classic(v1)。

还要提到很重要的一点,由于 Yarn 在过去有相当长一段时间的技术方案是要求开发者把 Yarn 的二进制文件提交进版本管理里,这招致了相当一部分开发者的反感,也无形中增加了 Yarn 新版推广的阻力,而在最新的版本中,他们终于放弃了这一要求

事实上,正是因为 Yarn 放弃了把二进制文件提交进版本管理 的做法,才促成了本文在 2024 年进行了重写,因为此前的内容传达了过时的信息。而在这之后,我谨慎乐观地认为开发者们至少更愿意尝试新的 Yarn 版本了。