Yarn 4.0 正式发布,性能大幅提升!

2023-10-27 10:39:28 浏览数 (2)

大家好,我是 ConardLi。

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

Breaking Change 速览

如果你现在使用的是 3.x ,下面这几点是需要了解的:

  • 目前要求 Node.js 18 版本;
  • 默认情况下,使用 yarn init 创建的新项目将不再启用 Zero-Install
  • 使用 yarn init 创建的新项目将使用 Corepack 而不是 yarnPath
  • 现在默认包含所有官方插件(typecript、interactive-tools ...)。
  • yarn workspaces foreach 命令语法略有改动

Corepack

自从 Yarn 2.0 版本以来,官方的建议是使用 yarnPath 设置来在每个项目中安装 Yarn(可以通过 yarn init -2yarn set version 自动设置)。

另外,过去还建议使用 yarnPath 设置指向一个已签入的二进制文件,但这种模式增加了一些不必要的麻烦,许多人不喜欢将二进制文件添加到他们的代码库中,即使很小。

为此,YarnNode.js 合作开发了一个名为 Corepack 的项目。

Corepack 是随 Node.js 16 一起提供的工具,它会根据你正在处理的项目自动选择正确的包管理器版本。

现在,Corepack 已经随着 Node 1820 一同发布,Yarn 不再依赖 yarnPathyarn init -2yarn set version 命令也已经更新,以便在可能的情况下更新 packageManager 字段。

Corepack 通过 package.json 中的标准 packageManager 字段可以知道要使用哪个包管理器版本。这个字段通常是通过 yarn init -2、yarn set version x.y.z 或更通用的 corepack use yarn@x.y.z 来设置的。

Hardened Mode

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 中需要启用它,可以通过环境变量将其打开:

代码语言:javascript复制
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

代码语言:javascript复制
module.exports = {
  async constraints({Yarn}) {
    for (const dep of Yarn.dependencies({ ident: 'react' })) {
      dep.update(`18.0.0`);
    }
  },
};

下面的约束会强制在所有工作区中正确设置 engines.node 字段:

代码语言:javascript复制
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 倍,这是由于新的包元数据缓存显着提高了重复安装的性能:

代码语言:javascript复制
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 的官网也迎来了全新的改版,包括新的命令、配置文档等。

最后

参考:

  • https://yarnpkg.com/
  • https://yarnpkg.com/getting-started
  • https://yarnpkg.com/blog/release/4.0

0 人点赞