简而言之:
当您在应用程序中安装依赖项时,只有您自己的 yarn.lock 文件会被尊重。依赖项中的锁定文件将被忽略。参考链接
首先,让我们澄清一些事情:
1. `--pure-lockfile` 和普通的 `yarn install` 是一样的,只是它不会生成 `yarn.lock` 文件,也不会更新已存在的文件。
2. Yarn 在安装时默认会从 `yarn.lock` 中读取以解析依赖关系,除非使用 `--no-lockfile` 参数。因此,没有必要告诉它从 `yarn.lock` 中读取。
那么,`yarn.lock` 的作用是什么呢?
`yarn.lock` 用于根据 `package.json` 中模块的 `semver version` 解析应该获取的版本。它并不用于确定模块应该解析到哪个 `semver version`。这简单来说并不是它的用途。
如在yarn文档中提到的:为了在不同的机器上获得一致的安装结果,Yarn需要比你在package.json中配置的依赖项更多的信息。Yarn需要存储每个依赖项的确切版本安装情况。
为了实现这一点,Yarn在项目的根目录中使用一个yarn.lock文件。
因此,为了解决依赖项的semver版本,yarn始终依赖于package.json。对于给定的semver版本,yarn会检查yarn.lock文件以确定应该获取哪个版本。这就是使yarn具有确定性的原因(npm也使用类似的技术,它使用npm-shrinkwrap.json文件)。
例子: Semver 版本如 ^1.2.4
可以解析为任何满足 >= 1.2.3 and < 2.0.0
的版本号。没有 yarn,npm 在一个机器上会安装 1.2.4
,但在另一个机器上会安装 1.9.9
,这取决于安装时的最新版本。这是 yarn 使用 yarn.lock
来解决的问题。
semver 版本
是由 package.json
文件确定的。而 yarn.lock
文件只是一个查找表,用于确定给定 semver 版本
号应安装哪个 版本 和 提交哈希。
yarn 如何根据 semver 版本解析模块的版本?
假设我们当前的 yarn.lock 文件如下所示:
bluebird@2.9.6:
version "2.9.6"
resolved "https://<...>/bluebird-2.9.6.tgz
bluebird@^2.9.30:
version "2.11.0"
resolved "https://<...>/bluebird-2.11.0.tgz
...
myModule@5.1.0:
version "5.1.0"
resolved "https://<...>/moduleA-5.1.0.tgz
dependencies:
bluebird "^1.0.0"
如果 package.json 中有
bluebird: "^2.9.30"
,yarn 会在锁定文件中查找一个条目
bluebird@^2.9.30
。它存在,因此被解析为
version=2.11.0
。
如果 package.json 中有
bluebird: "^2.9.0"
,yarn 会在锁定文件中查找一个条目
bluebird@^2.9.0
。它不存在。假设满足 semver 标准的最新稳定版本是
2.13.0
,那么 yarn 会添加一个条目
bluebird@^2.9.0
,解析为
2.13.0
。在解析 bluebird 的给定 semver 版本时,不管锁定文件中 moduleA 的依赖项中有什么条目,都不会受到影响。
Semver 版本
不受锁定文件中模块的依赖项映射的影响。
所以,如果 package.json 中有
bluebird: ""
,yarn 会在锁定文件中查找一个名为
bluebird@
的条目,但是找不到。因此,它会将
bluebird: ""
解析为最新版本,假设是
3.5.0
。现在,yarn 将添加一个条目
bluebird@
,解析为
3.5.0
。
bluebird@:
version "3.5.0"
resolved "https://<...>/bluebird-3.5.0.tgz
从现在开始,每当yarn遇到
{bluebird: ""}
,它将在lockfile中找到
bluebird@
的条目,因此将始终将其解析为
3.5.0
。
解决方案
要将
B: ""
解析为版本
1.0.0
,您需要在
yarn.lock
中有一个解析为
1.0.0
的
B@
的条目。一旦
yarn.lock
有了
B@
的条目,所有后续的安装都将始终获取
B=""
的版本
1.0.0
。
以下是您需要采取的步骤:
方法1(推荐)
如果您希望B解析为最新版本:
- 在A的package.json文件中添加
B:""
。
- 运行
yarn install
。这将添加一个解析为最新版本的B@
的条目。
- 推送
yarn.lock
文件。
- 从现在开始,无论谁运行
yarn install
都将获得相同的版本。
方法2
如果你希望B有一个旧版本:(强烈不推荐)
- 在 A 的 package.json 中添加
B: 1.0.0
。
- 运行
yarn install
。这将在 lockfile 中添加一个条目 B@1.0.0
。
- 在 yarn.lock 中将
B@
添加到 B@1.0.0
旁边。 B@, B@1.0.0: ...
- 在 A 的 package.json 中将 B 的版本更改为
""
。
- 推送
yarn.lock
文件。
- 从现在开始,无论谁运行
yarn install
,都会得到 B 的版本为 1.0.0
。
这种方法非常危险,因为很容易出错。你的 yarn.lock 文件应该始终由 yarn 管理。
方法 3(推荐)
如果你希望 B 保持在 1.0.0 版本
- 在 A 的 package.json 文件中将 B 的版本修复为
1.0.0
。
- 运行
yarn install
。这将在 lockfile 中添加一个条目 B@1.0.0
。
- 推送 yarn.lock 文件。
- 从现在开始,无论谁运行
yarn install
,都会得到 B 的版本为 1.0.0
。
编辑:使用依赖项中的 yarn.lock 文件
如果你查看这个 doc,他们明确提到 yarn 只会使用顶层的 yarn.lock 文件,并忽略依赖项中存在的锁文件。
目前没有办法使用依赖项中的 yarn.lock 文件锁定二级依赖项。我认为这并不需要。事实上,yarn 的创建者在 这里 解释了为什么这样做。原因如下:
- 如我之前所解释的那样,顶层的yarn.lock文件可以很好地捕捉到要安装的二级依赖的版本。
- 当直接使用子依赖项时,您将无法更新子依赖项的版本,因为它们将被其他yarn.lock文件锁定。您可以通过我对yarn如何解析依赖关系的解释来验证这一点。
- Yarn无法折叠(去重)依赖项,以便兼容的版本范围只安装一个版本。
此外,就像在您的用例中一样,如果A有一个只能与版本1.0.0
一起使用的依赖项B,A的package.json应该将B的版本指定为1.0.0
而不是“”。您可以始终修复顶层的yarn.lock
,添加一个解析为1.0.0
的B@
条目,但不建议手动修复yarn.lock文件,如我之前所提到的。