大家好,我是 ConardLi。
10.23 日,Yarn 团队经过一年多的努力,中间经过了 53 个候选版本,终于发布了 Yarn 4.x 的稳定发行版本。

如果你现在使用的是 3.x ,下面这几点是需要了解的:
Node.js 18+ 版本;yarn init 创建的新项目将不再启用 Zero-Install;yarn init 创建的新项目将使用 Corepack 而不是 yarnPath;typecript、interactive-tools ...)。yarn workspaces foreach 命令语法略有改动自从 Yarn 2.0 版本以来,官方的建议是使用 yarnPath 设置来在每个项目中安装 Yarn(可以通过 yarn init -2 或 yarn set version 自动设置)。
另外,过去还建议使用 yarnPath 设置指向一个已签入的二进制文件,但这种模式增加了一些不必要的麻烦,许多人不喜欢将二进制文件添加到他们的代码库中,即使很小。
为此,Yarn 与 Node.js 合作开发了一个名为 Corepack 的项目。
Corepack 是随 Node.js 16+ 一起提供的工具,它会根据你正在处理的项目自动选择正确的包管理器版本。

现在,Corepack 已经随着 Node 18 和 20 一同发布,Yarn 不再依赖 yarnPath。yarn init -2 和 yarn set version 命令也已经更新,以便在可能的情况下更新 packageManager 字段。
Corepack通过package.json中的标准packageManager字段可以知道要使用哪个包管理器版本。这个字段通常是通过yarn init -2、yarn set version x.y.z或更通用的corepack use yarn@x.y.z来设置的。
Yarn 新增了一个模式(Hardened Mode)来试图保护用户免受一些常见的攻击。
在此模式下运行时,Yarn 将执行两个额外的验证:
lock文件中 存储的解析规则是否与范围所能解析到的版本一致。当我们在项目中定义了依赖项的范围(例如使用 "^" 或 "~" 等符号指定的版本范围),
Yarn会根据这些范围来解析并选择合适的版本安装到项目中。但是,有时在解析依赖项时可能会出现问题,例如范围可能无法解析到满足所有依赖项的兼容版本,或者范围太宽松导致安装了过多的依赖项。
lock 文件中存储的 npm 包元 metadata 是否与远程注册表中的 metadata 一致。这些操作其实就是用来防止某些攻击者可能使用 Yarn 对我们的项目进行 PR 时暗中修改我们的 lock 文件。

我们可以通过
enableHardenedMode来主动启用Hardened Mode,但当Yarn检测到它在公共存储库上的GitHub Pull Request中运行时,它也会自动启用。可以通过在yarnrc文件中显式关闭enableHardenedMode来禁用此功能。
另外,在 Hardened Mode 约束下运行的安装会比平常慢得多,因为它们需要执行许多额外的网络请求,所以不建议默认启用它。
如果需要在特定的 CI Job 中需要启用它,可以通过环境变量将其打开:
export YARN_ENABLE_HARDENED_MODE=1
Yarn 是目前唯一实现约束引擎的包管理器,这个功能可让我们定义项目必须满足的一组规则。
假设我们的项目中有两个工作区(Workspaces):A 和 B,并且它们都依赖于同一个包,比如 "lodash"。
在以前的版本中,如果工作区 A 依赖于 "lodash@^3.0.0",而工作区 B 依赖于 "lodash@^4.0.0",Yarn 会允许这种情况,并在安装依赖时分别安装 "lodash@^3.0.0" 和 "lodash@^4.0.0"。
然而,有时这样的情况会导致冲突和问题。为了解决这个问题,Yarn 引入了 JavaScript 约束引擎。
使用 JavaScript 约束引擎,我们可以定义一些规则来限制工作区之间依赖项的版本关系。例如,可以定义一个规则,要求所有工作区都必须使用相同的 "lodash" 版本。
Yarn 的约束引擎过去由 Tau-Prolog(一种 JavaScript Prolog 实现)提供支持。与 JavaScript 等命令式语言不同,Prolog 使用一种称为逻辑编程的不同模型 - 如果规则为真,则定义某个事物存在。这是一个非常有趣的模式,与基于规则的 linting 概念很好地结合在一起。但不幸的是,Prolog 被证明使用起来非常复杂,增加了约束的学习曲线,超出了大家可以接受的阈值。
因此,从 Yarn 4 开始,Prolog 约束已被弃用,并且已被基于 JavaScript 的全新引擎所取代,并具有可选的 TypeScript 支持!
比如下面这个简单的例子,yarn.config.cjs 将强制所有 react 依赖项设置为 18.0.0。
module.exports = {
async constraints({Yarn}) {
for (const dep of Yarn.dependencies({ ident: 'react' })) {
dep.update(`18.0.0`);
}
},
};
下面的约束会强制在所有工作区中正确设置 engines.node 字段:
module.exports = {
async constraints({Yarn}) {
for (const workspace of Yarn.workspaces()) {
workspace.set('engines.node', `20.0.0`);
}
},
};
为了更好地传达信息,CLI 界面的的多个部分得到了改进。例如,yarn install 现在会告诉我们添加了哪些新的软件包,以及它们的总重量。另外,它不会再像以前那样打印与同级依赖关系相关的警告,现在只在可操作的情况下打印警告:

另一个例子是 yarn config 命令,它会显示一个新的树形显示,现在还接受任意数量的设置作为位置参数,让我们选择您希望看到的内容:

4.0 在安装速度上明显快于 3.6。例如,以下是从缓存安装 Gatsby 及其约 350MB 依赖关系树的时间差。性能提高了 3 倍,这是由于新的包元数据缓存显着提高了重复安装的性能:
hyperfine -L v stable,canary --prepare 'rm -rf ~/.yarn/berry/cache' 'cd $(mktemp -d) && yarn init -2 && yarn set version {v} && yarn && yarn add gatsby --mode=skip-build'

目前 Yarn 大多数情况下基本上和 pnpm 的性能接近了。
另外,Yarn 的官网也迎来了全新的改版,包括新的命令、配置文档等。

参考: