首页 前端知识 yarn.lock 文件介绍

yarn.lock 文件介绍

2024-04-30 12:04:56 前端知识 前端哥 489 453 我要收藏

这里填写标题

  • 1. yarn.lock 文件介绍
    • 1.1. package-lock.json 和 yarn.lock
    • 1.2. yarn.lock
    • 1.3. 问题引出
    • 1.4. yarn.lock 的作用?
    • 1.5. yarn.lock 长啥样?
      • 1.5.1. Identifier(s)
      • 1.5.2. version
      • 1.5.3. resolved
      • 1.5.4. integrity
      • 1.5.5. dependencies
    • 1.6. yarn.lock 是如何生成的?
    • 1.7. 依赖管理
    • 1.8. 霸道的 resolutions
    • 1.9. 如何避免出现问题?
      • 1.9.1. yarn.lock 与 package.json 不 match
        • 1.9.1.1. 场景
        • 1.9.1.2. 问题
        • 1.9.1.3. 如何解决
        • 1.9.1.4. 如何预防
      • 1.9.2. 把 lock file 删掉, 整个重装
        • 1.9.2.1. 场景
        • 1.9.2.2. 问题
        • 1.9.2.3. 解决思路
    • 1.10. 参考资料
    • 1.11. 扩展阅读

1. yarn.lock 文件介绍

1.1. package-lock.json 和 yarn.lock

NPM 和 YARN 是两个不同的包管理系统, 其中 NPM 生成 package-lock.json, YARN 生成 yarn.lock, 这两个文件记录当前项目所依赖的各个包的版本。

最安全的做法是在每次依赖关系发生变化时生成并提交它们。 但是, 这可能很麻烦, 或者两个文件可能不同步。所以现在的一般做法是只保留其中一个文件, 忽略另外一个文件, 这取决于当前使用的包管理系统。

推荐使用 YARN, 即保留 yarn.lock, 而且每次变动需要提交该文件。

1.2. yarn.lock

官方对 yarn.lock 文件的说明如下:

为了跨机器安装得到一致的结果, Yarn 需要比你配置在 package.json 中的依赖列表更多的信息。 Yarn 需要准确存储每个安装的依赖是哪个版本。
为了做到这样, Yarn 使用一个你项目根目录里的 yarn.lock 文件。这可以媲美其他像 Bundler 或 Cargo 这样的包管理器的 lockfiles。它类似于 npm 的 npm-shrinkwrap.json, 然而他并不是有损的并且它能创建可重现的结果。

需要注意的是: 所有 yarn.lock 文件应该被提交到版本控制系统。

1.3. 问题引出

还原一下我出过的 Case 项目里原本有个依赖 foo:

  • package.json 里定义的 foo@^1.0.1
  • yarn.lock 里的版本是 1.0.1

同学 A 是负责 foo 这个库的开发, 一次发版后, 到项目里升级这个依赖到 1.1.0, 但是提交代码时, 只变更了 package.json, 没有更新 yarn.lock

  • package.json foo@^1.0.1``foo@^1.1.0
  • yarn.lock 没变, 还是 1.0.1

然后大家每次拉新代码并安装依赖后, 本地总有个烦人的 yarn.lock 文件变更, 大家心想应该是有人升级依赖的时候忘记提交 yarn.lock 了于是同学 B 行动了:

  • 先看了下 foo 这个库现在有哪些版本, 最新版本是 1.1.2, 跟 package.json 里定义的 ^1.1.0 差了两个版本, 不能保证线上是 1.1.0, 因为每次上线, 都会去找符合 ^1.1.0 这个 version range 里的最新版本
  • 所以去看了下最后一次上线的构建日志, 发现下载的是 1.1.2
  • 于是提交了 yarn.lock, 把版本锁在了 1.1.2

然后过了一天, 拉群了

  • 1.1.2 版本有 bug, 修复后发布了 1.1.3
  • 但是项目里, 由于 B 把版本锁在了 1.1.2, 锁住了 bug
  • 同学 A 质问 B 为什么要锁别人的库的版本
  • 这个 case 记了几个 TODO
    • 因为没有提交 yarn.lock, 不确定同学 A 是通过 yarn upgrade 升级的版本, 还是手动去改了 package.json, 所以——不要手动修改 package.json 升级版本
    • 升级依赖后, 一定要同时提交 package.json 和 yarn.lock

1.4. yarn.lock 的作用?

锁定唯一版本!

  • package.json 里定义的是版本区间, 如 ^1.0.0
  • 而 yarn.lock 里的 version 字段是唯一的版本号, 如 1.0.0

1.5. yarn.lock 长啥样?

里面都是一块一块的, 每一块大概长下面这样:

core-js-compat@^3.0.0:
  version "3.14.0"
  resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.14.0.tgz#b574dabf29184681d5b16357bd33d104df3d29a5"
  integrity sha1-tXTavykYRoHVsWNXvTPRBN89KaU=
  dependencies:
    browserslist "^4.16.6"
    semver "7.0.0"

1.5.1. Identifier(s)

第一行的 core-js-compat@^3.0.0 是依赖的 identifier。和 package.json 里对应的包名和版本区间, 用 @ 连接。这边的标题里带了 (s), 是因为多个 Identifier 最终可能都指向同一个版本 (具体例子可以看下文 ### dependencies 里给出的例子)

1.5.2. version

第二行 version 是实际安装的版本。通常是满足版本区间里的一个版本, 比如上一行 identifier 里版本区间是 ^3.0.0, 这里实际安装的是 3.14.0, 符合要求。但是为什么要说是 “通常” 呢, 因为有例外, 在后文 ### resolutions 部分会讲到。

1.5.3. resolved

第三行 resolved 的是一个链接, 是下载这个包的地址。这个 url 里的域名部分跟项目里配置的 .npmrc 或你本地的 npm 配置的 registry 有关。

1.5.4. integrity

第四行 integrity 是对 resolved 下载下来的文件进行完整性校验。如果出现 diff, 说明同一个下载链接对应的文件被修改过。

1.5.5. dependencies

第五行 dependencies 是这个包自己的依赖。如这里依赖的 browserslist "^4.16.6", 你想看下实际安装的哪个版本, 就可以把它拼成 Identifierbrowserslist@^4.16.6", 以此为关键字在 yarn.lock 中搜索, 就能找到对应的 “块” 了。

browserslist@4.16.6, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6, browserslist@^4.3.6, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.7.2, browserslist@^4.9.1:
  version "4.16.6"
  resolved "https://https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
  integrity sha1-15ASd6WojlVO0wWxg+ybDAj2b6I=
  dependencies:
    caniuse-lite "^1.0.30001219"
    colorette "^1.2.2"
    electron-to-chromium "^1.3.723"
    escalade "^3.1.1"
    node-releases "^1.1.71"

上面这个例子第一行有多个 Identifiers, 最终都指向第二行的 version "4.16.6", 可以检查下 4.16.6 版本满足上面所有 Identifiers 里的版本区间: 4.16.6^4.0.0

1.6. yarn.lock 是如何生成的?

yarn.lock 是自动生成的, 你不应该去手动的修改

1.7. 依赖管理

比如我们的常规操作, 都会自动更新 package.json 和 yarn.lock

  • 新增依赖: yarn add
  • 升级依赖: yarn upgrade

更多可参考: https://classic.yarnpkg.com/en/docs/managing-dependencies

1.8. 霸道的 resolutions

假如你的项目依赖了 foo,foo 依赖了 bar@^1.0.0。假设 bar 现在有两个版本 1.0.0 和 1.1.0。很不幸, bar 在发布 1.1.0 的时候没有做好向后兼容。导致 foo 和 bar@1.1.0 不能搭配使用。如果你可以等:

  • 要么等 foo 把依赖 bar 锁成 1.0.0 并重新发版
  • 要么等 bar 修复兼容问题后重新发版

那如果你等不了呢, 你已知 foo 和 bar@1.0.0 可以正常工作。如果你能锁住 foo 对 bar 的依赖就好了, 但是这定义在 foo 的 packge.json 里, 你总不能去改 node_modules/foo/package.json 吧? 这不合适。resolutions 可以解决你的问题, 只要在你自己项目的 package.json 里定义:

"resolutions": {
  "foo/bar": "1.0.0"
}

这里的 key"foo/bar" 表示 foo 的直接依赖 bar, 把版本区间重写成 1.0.0。如果 foo 不是直接依赖的 bar(foo -> ... -> bar), 我还需要把中间的链路都捋清楚吗? 不用那么麻烦!

"resolutions": {
  "foo/**/bar": "1.0.0"
}

如果你的项目里有很多依赖直接 / 间接的依赖了 bar, 每个定义的版本区间可能有差别, 你知道某个版本可以让他们都能正常工作, 而不用安装多个版本。也可以不用声明前缀部分, 只写包名 bar。这样不管是哪里依赖到了 bar 都会指向你声明的哪个版本。

"resolutions": {
  "bar": "1.0.0"
}

执行 yarn install 后, 在 yarn.lock 里搜索 bar@:

bar@^1.0.0 bar@1.1.0 bar@^2.0.0:
  version "1.0.0"
  ...

可以看到, resolutions 可以违背版本区间的限制, 比如上例中 Identifiers 里的 bar@1.1.0``bar@^2.0.0

1.9. 如何避免出现问题?

1.9.1. yarn.lock 与 package.json 不 match

1.9.1.1. 场景

只改动 package.json, 忘记提交 yarn.lock

1.9.1.2. 问题

执行 yarn install 后, yarn.lock 有变更

1.9.1.3. 如何解决
  • 解决掉引入问题的人 (PEACE & LOVE)
  • 确认 diff 并提交变更后的 yarn.lock
    • 确定是哪些依赖产生的 diff, 并回归相关功能 (成本有点大, 而且如果依赖关系比较复杂, 那是很难确认影响面的)
    • OR 成最后一次上线的版本 (⚠️ 可能会住 bug )

可以看到出现问题再解决还是很棘手的, 而且有一定赌的成分, 所以我们最好预防。

1.9.1.4. 如何预防

即使现在项目是好的, 我们是不是也应该防患于未然!

  • 开发的同学 &&CR 共同把关

  • 阻塞构建 (有以下几种方案可选)

    • npm ci 与 npm install 类似, 但是在安装依赖的过程中如果发现 package-lock.json 不匹配, 则会抛错并退出, 而不去更新 lock file
    • yarn install --frozen-lockfile 等价于 npm ci, 但是在测试过程中发现几个问题:
      • resolutions 里修改版本, 不会报错
      • Classic yarn (version 1) 在 package.json 里移除依赖, 也不会报错 (v2 修复了这个问题, 详见 https://github.com/yarnpkg/yarn/issues/5840)
  • yarn install && git diff--exit-codeyarn.lock 正常执行 install 命令安装依赖, 再检查 lock file 有无 diff

    • 优点: 简单粗暴 && 直观 (不会出现因为对命令 or 参数理解存在误差造成不符合预期的情况)
    • 缺点: 慢! 效率低! 因为会把包括需要更新的依赖也下载完, 本来应该在检测到需要更新的时候就停止的 (目前没有想到什么好办法)

1.9.2. 把 lock file 删掉, 整个重装

1.9.2.1. 场景

当你更新了某个依赖后, 发现项目跑不起来了, 推测可能是依赖的问题。有没有尝试过把 yarn.lock + node_modules 都删了重新安装, 幸运的话可能 “问题就解决了”(解决了, 但是好像没完全解决, 反正项目跑起来了)

1.9.2.2. 问题

把 yarn.lock 删掉后, 原本锁住的版本都放开了, 执行 yarn install 的时候会根据 package.json 里定义的版本区间去找最新版。所以, 可能会造成你预期外的依赖也被更新了, 不幸的话可能会引入 bug。

1.9.2.3. 解决思路

可以单独搞一个依赖 empty-lock-lock:

  • 什么都不做 (一个空的库), 发版 1.0.0
  • 定义一个 postinstall 脚本, 直接抛错, 发版 1.0.1

在项目中安装依赖 yarn add empty-lock-lock@1.0.0 --dev, yarn.lock 里会锁定版本为 1.0.0。然后准备一个陷阱:

  • 手动把 package.json 里的版本改成区间 ^1.0.0
  • 手动修改 yarn.lock 里, 把 Identifier 部分的 empty-lock-lock@1.0.0 也替换成 empty-lock-lock@^1.0.0

修改后提交, 可以再执行 yarn install 验证下, yarn.lock 没有 diff, 证明我们手动修改后的 package.json 和 yarn.lock 仍然是 match 的。等小白鼠上钩, 如果把 yarn.lock 整个删掉了, 再执行 yarn install, 安装到 empty-lock-lock 的时候, 会根据 package.json 里定义的 ^1.0.0 版本区间里找最新的, 这时候会找到 1.0.1 版本, 下载后触发 postinstall 就报错啦!

1.10. 参考资料

  • https://robertcooper.me/post/how-yarn-lock-files-work-and-upgrading-dependencies

1.11. 扩展阅读

  • package.json 中定义的各种依赖: https://classic.yarnpkg.com/en/docs/package-json
转载请注明出处或者链接地址:https://www.qianduange.cn//article/6749.html
标签
yarn
评论
会员中心 联系我 留言建议 回顶部
复制成功!