Yarn 在前端开发者中必然不陌生,目前来说大部分使用的都是 Yarn 1.x 的版本,其实 Yarn 1.x 很多时候定位有些尴尬,在设计上还是从 npm 上借鉴了很多,甚至在大多开发者眼中只是将package-lock.json换成了yarn.lock而已(yarn workspaces是较大的不同),虽然号称在速度上有优势,但 npm 所具有的缺点 Yarn 1.x 还是不可避免地会存在。

所以促使了 Yarn 2 地出现,在尽可能保留 1.x 的使用方式和功能的同时,还提出和实现/重构了很多新的功能,目前 1.x 已经不再进行版本更新了,并且 1.x 的文档也已经转移到https://classic.yarnpkg.com/。本文不会详细介绍Yarn 2 引入了哪些新的功能,仅从安装、使用以及现在的开发模式的角度了解 Yarn 2,具体新功能可以到ChangelogFeatures中查看。

安装 Yarn 2

在维护多个项目时经常遇到 npm 版本不统一的情况,所以我们经常需要在同一台机器上安装多个Node.js,所以nvmnvm-windows出现了,但作为开发者仍要记得在不同项目间切换版本。而Yarn 2 认为自己本身就应该是项目的依赖,甚至版本也应该被锁定(当然可以升级),所以 Yarn 2 会将自己维护到项目中,目前仍需要借助yarn 1完成安装:

npm install -g yarn # 全局安装yarn 1
yarn --version # A

虽然看起来有些怪异,但也能理解,毕竟一个如此普及的工具不可能不考虑兼容性和迁移难度就进行完全的覆盖,Yarn 2 的出现也要保证原有 Yarn 1 的正常使用。

工程中升级为 Yarn 2

前面说到 Yarn 2 是把自己作为项目的一个普通依赖看待的,所以升级 Yarn 2 也是针对项目而言的:

cd /path/to/project
# 上面A行得到的版本如果是1.22+
yarn set version berry # B1

# 上面A行得到的版本如果低于1.22
yarn policies set-version berry # B2

执行完 B1 或 B2 行之后,工程根目录下会新增一个.yarn目录及一个.yarnrc.yml的配置文件:

  • .yarn目录:存放 Yarn 2 的具体执行文件、由 Yarn 2 安装的依赖等
  • .yarnrc.yml配置文件:此工程针对 Yarn 2 的具体配置文件,和.npmrc.yarnrc功能类似,这里要注意Yarn 2 不会再读取.npmrc.yarnrc中的配置,同时文件扩展也必须是 yml,.yarnrc不能生效
# .yarn目录初始结构
.yarn
├── releases
│   └── yarn-berry.js
# .yarnrc.yml初始结构
yarnPath: ".yarn/releases/yarn-berry.js"

.yarn.yarnrc.yml都应该提交到仓库。

升级 Yarn 2 版本

由于 Yarn 2 目前仍在快速迭代中,为了修复问题或者想要体验新功能时,升级 Yarn 2 的版本也是个常规操作,所幸升级 Yarn 2 版本也很容易,而且提供了多种方式:

# 升级至最新的发布版本 - 大众
yarn set version latest

# 从master分支升级版本 - 尝鲜
yarn set version from sources

# 从其他分支升级版本 - 需要解决特定需求
yarn set version from sources --branch 1211

上面是从Yarn 2 仓库中直接更新到你的工程中,升级完成后一般需要通过yarnyarn install更新依赖。

按照上面的步骤安装/更新 Yarn 2 后,在当前工程下应该就能够正常运行 Yarn 2 了,可以通过yarn --version来确认当前是否是 2.x 版本。

使用 Yarn 2 管理依赖

Yarn 2 一些常见的使用方式和 Yarn 1 没什么区别,比如:

# 安装依赖
yarn
yarn install

# 添加依赖
yarn add [package] [--dev|--peer]

# 删除依赖
yarn remove [package]

当然也有新增/替换的一些命令:如yarn up用于更新依赖(Yarn 1.x 中的yarn upgrade)、yarn dlx用于临时执行依赖(和npx类似),具体可查看CLI 文档

这里我们重点关注一下依赖的安装,如果我们现在在工程中进行依赖安装可能会傻眼:

  • 多了个.pnp.js的文件
  • .yarn目录下多了很多文件
  • node_modules目录被清空了
  • 工程可能也跑不起来了

.pnp.js.yarn目录新增文件意义

这就不得不提到 Yarn 2 的默认使用的是pnp的方式安装依赖,和传统的node_modules方式管理依赖相比最重要的不同是所有依赖是以 zip 包的方式存在,默认存放在.yarn/cache目录下。而以 zip 包的方式管理依赖就会有个致命的问题:我们的代码识别不了,更别提找到对应的依赖文件了,所以 Yarn 2 为我们生成了.pnp.js文件解决这个问题。至于采用这种方式的优点这里简单列举一下,有兴趣的可以看下官方解释

Yarn 2 认为工程依赖的锁定不仅仅只依靠yarn.lock达到,而应该将依赖也提交到仓库,所以.yarn目录都应该被提交到仓库。而这在传统的node_modules方式下是不可行的,不仅是因为更新后产生的变更数量,更因为一个稍微大些的工程node_moduels的文件数量和体积都很容易达到 Git 无法处理的地步。而因为 Yarn 2 使用 zip 包的方式管理依赖,大大减少了文件数量和文件体积,故而使得工程的依赖能够进行版本管理,进而能够完美地实现依赖锁定

在目前的工程协作开发中,协作者拉取代码后,第一步就要进行依赖的安装工作,但 Yarn 2 改变了这个境况,既然工程依赖都被提交到仓库中了,协作者拉取代码后甚至都不再需要执行依赖安装的步骤,因为依赖已经在那里了,这就是 Yarn 2 提出的零安装 zero-install。这也顺带解决了诸如服务器网络限制不能下载依赖新分支升级依赖老分支不能正常运行等问题。

工程跑不起来的解决方案

那是不是有了.pnp.js我们的工程就能完全正常了呢?很遗憾也没这么简单:目前很多工具还没有实现Plug'n'Play规范,所以目前很可能你的工程目前仍然跑不起来,那是不是就意味着我们不能用 Yarn 2 了呢?当然不是,Yarn 2 提供了两种安装依赖的方式:pnp(默认)和node-modules(没有写错,不是 node_modules),配置方式:

# .yarnrc.yml
yarnPath: ".yarn/releases/yarn-berry.js"

# 如果工程跑不起来可以先尝试增加下面的配置:
nodeLinker: "pnp"
pnpMode: "loose"

# 如果仍然跑不起来,可以用下面的配置完全按照以前的依赖按照方式
nodeLinker: "node-modules"

nodeLinker如果是”pnp”的话(默认情况),pnpMode默认为”strict”,这种情况下所有用到的依赖都必须显式地声明在package.json中,也就是说如果你的模块声明了 webpack 作为依赖,webpack 中声明了 acorn 作为依赖,在”strict”模式下你的模块不能直接引入 acron,否则会报错。而”loose”模式下是允许的,但却不是推荐用法。

nodeLinker设置为”node-modules”就和 Yarn 1 和 npm 的方式安装依赖没什么区别了,所有依赖仍然存在于node_moduels目录下,这些情况下也不会生成.pnp.js文件,因为不需要从 zip 包中解析依赖了。

有部分有名的前端工具支持了,具体可查看支持列表,随着时间推移相信会有越来越多地支持。

Yarn 2 配置

Yarn 2 配置可以通过yarn config CLI 完成,也可以通过直接编辑.yarnrc.yml文件完成,有些配置和 Yarn 1.x 的配置差异挺大,全部的配置可查阅官方配置文档,这里以私有域的配置做一下示例:

# 使用yarn config
yarn config set --json npmScopes '{"lwz": {"npmRegistryServer": "http://npm.lwz.com"}}'
yarn config set --json unsafeHttpWhitelist '["*.lwz.com", "lwz.com"]'

使用--json允许复杂的配置,否则只能配置 string、number 和 boolean 类型的配置项

当私有域名不是 https 时,需要增加unsafeHttpWhitelist配置添加私有域名,可以使用通配符

对应在.yarnrc.yml中:

# .yarnrc.yml
npmScopes:
  lwz:
    npmRegistryServer: "http://npm.lwz.com"

unsafeHttpWhitelist:
  - "*.lwz.com"
  - "lwz.com"

复杂的配置推荐在.yarnrc.yml中直接编辑,使用yarn config set设置已存在的配置是一个覆盖的过程,所以如果想要保留之前的配置,还需要在配置时将之前的配置带上。

总结

本文仅站在使用者的角度说明如何使用 Yarn 2,对其新特性、优势等方面并未深入讲解,这些方面留待日后讲解,如果想要深入了解可到官方文档查阅。