pnpm.lock.yaml,看似无关紧要,实则······

2,654 阅读9分钟
  • 前言

    pnpm-lock.yarm:我就是童脸狼,表面上单纯天真,实际上圆滑 通透。你不可能算计得了我,因为从一开始你 就被我布局了。我是棋手,而你只是棋子,若 你违逆我,你会知道什么是残酷和黑暗。当我 重临世界之日,诸逆臣皆当死去!

    咳咳,言归正传,pnpm-lock.yarn差不多就是这样,看样子不起眼,其实这是一个不可忽视的文件,也是一个非常巧妙的设计,笔者也是最近在学习工程化,注意到了这个文件。

    pnpm-lock-yarm是什么?

    yarm文件

    判断一个男人什么档次,就看他开什么车,开玛莎拉蒂一般是总裁,开红旗一般是当官的,像笔者这样开单车的一般是穷B。。。判断一个文件干什么的,先看后缀。

    YAMLYAML Ain't Markup Language)是一种人类可读的数据序列化格式,通常用于配置文件、数据交换等场景。YAML 文件(通常使用 .yaml.yml 扩展名)是一种用于表示结构化数据的文本文件,它非常适合配置文件、数据存储以及与程序之间的数据交换。

    YAML 文件基本语法
    1. 键值对:YAML 使用键值对来表示数据。键和值之间用冒号 : 隔开。

      name: "John"
      age: 30
      
    2. 嵌套结构:YAML 使用 缩进 来表示数据的层级关系,通常使用 2 个空格来缩进。注意,YAML 不允许使用制表符(Tab),必须使用空格来缩进。

      person:
        name: "John"
        age: 30
        address:
          street: "123 Main St"
          city: "New York"
      
    3. 列表:列表(数组)使用连字符 - 来表示,每个元素占一行,缩进表示元素属于同一个列表。

      fruits:
        - Apple
        - Banana
        - Orange
      
    4. 注释:YAML 中的注释以 # 开头,注释内容不会被解析。

      # This is a comment
      name: "John"  # Inline comment
      
    5. 字符串和数字:YAML 中的字符串可以不用加引号,但如果字符串中有特殊字符(如空格),则需要加引号。数字直接写就可以,YAML 会自动解析为整数或浮动类型。

      string: "Hello, World"
      age: 30
      height: 5.9
      
    6. 多行字符串:YAML 支持多行字符串,使用 |> 来表示不同的格式:

      • |:保留换行符。
      • >:将换行符转换为空格,适合长文本。
      description: |
        This is a multi-line string.
        It preserves the newlines.
      ​
      summary: >
        This is a multi-line string.
        But newlines are replaced by spaces.
      

    pnpm-lock-yarm的作用

    pnpm-lock.yaml 文件是 pnpm 包管理工具生成的一个 锁定文件,它在项目中起着重要的作用,类似于 package-lock.json(在 npm 中)和 yarn.lock(在 Yarn 中)。它的作用是确保所有开发者、CI/CD 环境和生产环境中安装的依赖版本一致,从而避免不同开发环境中的依赖版本差异导致的问题。

    pnpm-lock.yaml 文件是 pnpm 包管理工具生成的一个 锁定文件,它在项目中起着重要的作用,类似于 package-lock.json(在 npm 中)和 yarn.lock(在 Yarn 中)。它的作用是确保所有开发者、CI/CD 环境和生产环境中安装的依赖版本一致,从而避免不同开发环境中的依赖版本差异导致的问题。

    1. 锁定依赖版本

    pnpm-lock.yaml 文件的最主要作用是 锁定依赖的具体版本。在你的 package.json 中,依赖通常是通过 版本范围(如 ^1.0.0~1.0.0)来指定的,这意味着包管理工具(如 pnpm)会选择符合该版本范围的 最新版本。但是,由于这个版本范围可能会随着时间的推移而变化,为了确保团队中的每个成员、CI/CD 和生产环境中的依赖版本保持一致,pnpm-lock.yaml 文件会记录下 实际安装的每个依赖的具体版本

    示例:

    假设 package.json 中的某个依赖指定为 lodash: "~4.17.0"。运行 pnpm install 后,pnpm 会选择一个符合这个版本范围的最新版本(比如 4.17.21)。然后,这个具体版本会被锁定在 pnpm-lock.yaml 中,确保后续所有人安装的都是 4.17.21 而非 4.18.0 或其他版本。

    lodash@^4.17.0:
      version: 4.17.21
      resolution: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
      integrity: sha512-xyz...
      engines:
        node: ">=0.10.0"
    
    2. 确保依赖一致性

    pnpm-lock.yaml 文件确保了 跨开发环境的依赖一致性。假设团队中的开发者 A 和 B 都在使用相同的 package.json,但是他们的本地 node_modules 中的依赖版本不同(可能是因为不同的安装时间或者网络问题),那么 pnpm-lock.yaml 就可以保证在每个开发者机器上都安装完全相同版本的依赖。这样做避免了 “它在我机器上可以正常工作,但在其他机器上不行” 这样的情况。

    • 当开发者 A 执行 pnpm install 时,pnpm 会参考 pnpm-lock.yaml 文件来安装所有依赖,并锁定为具体版本。
    • 当开发者 B 执行 pnpm install 时,pnpm 会自动按照 pnpm-lock.yaml 中记录的具体版本来安装依赖。
    3. 加速安装过程

    pnpm-lock.yaml 使得依赖安装过程更加 高效。因为 pnpm 可以直接读取锁文件中记录的依赖树结构和版本信息,避免了每次都需要重新解析和计算依赖的版本。这样可以显著提升安装速度,尤其是在大项目中。

    同时,pnpm-lock.yaml 还可以缓存和复用已经安装的依赖(尤其是在 CI/CD 环境中),进一步提高安装效率。

    4. 管理嵌套依赖(子依赖)

    pnpm-lock.yaml 还负责记录项目中所有 直接依赖间接依赖(子依赖、嵌套依赖)的具体版本信息。例如,如果你安装了 lodash,而 lodash 本身又依赖了其他包,pnpm-lock.yaml 会记录所有的依赖层级及其具体版本。

    场景:

    1. 安装 lodash: 你在项目中运行了命令 pnpm add lodash,这会将 lodash 安装为你的直接依赖。
    2. lodash 的子依赖: lodash 本身依赖其他包,如 lodash.merge,可能会有多个版本或不同的包版本需求。
    3. 生成的 pnpm-lock.yaml: pnpm-lock.yaml 会记录这些信息。它不仅会记录你直接安装的 lodash 的具体版本,还会记录 lodash.merge 的具体版本及其任何其他子依赖。

    示例

    假设 lodash 版本是 4.17.21,而它依赖 lodash.merge 版本是 4.6.0,在 pnpm-lock.yaml 中可能会看到类似这样的内容:

    dependencies:
      lodash: 4.17.21
      lodash.merge: 4.6.0
    
    lockfileVersion: 5
    packages:
      /lodash/4.17.21:
        resolution: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
        dev: false
        dependencies:
          lodash.merge: 4.6.0
      /lodash.merge/4.6.0:
        resolution: "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz"
        dev: false
    
    

    这样确保了项目中的每个子依赖也是固定版本,避免了不同开发者机器上依赖版本不一致的问题。

    5. 支持树形结构和多版本依赖

    由于 pnpm 使用一种独特的 符号链接结构(而非像 npm 那样将所有依赖复制到 node_modules),pnpm-lock.yaml 也需要记录 多版本依赖 的情况。例如,如果两个直接依赖都依赖于不同版本的某个库,pnpm-lock.yaml 会确保这两个版本能够并存,并且能够正确地安装到不同的文件夹中。

    lodash@4.17.21:
      dependencies:
        lodash@4.17.21:
          version: 4.17.21
    
    6. CI/CD 环境的一致性

    在 CI/CD 流程中,pnpm-lock.yaml 文件确保了你在本地开发时安装的依赖版本与在生产环境或 CI/CD 环境中安装的版本完全一致。即使在不同的机器和不同的时间运行 pnpm install,只要 pnpm-lock.yaml 保持不变,依赖版本就能保持一致。

    与package.json的关系?

    聊pnpm-lock.yaml文件,就绕不开package.json,就像通辽不能失去耶路撒冷一样

    package.json是什么?

    package.json 是 Node.js 项目的一个核心文件,用于管理项目的元数据(如项目名称、版本、描述等),以及项目所需的依赖、脚本命令、配置等。它通常位于 Node.js 项目的根目录下。

    以下是 package.json 文件中的一些常见字段:

    1. name
    • 项目名称。通常是一个小写字母的字符串,多个单词可以用连字符 - 隔开。
    • 示例:"name": "my-project"
    2. version
    3. description
    • 对项目的简短描述,通常是一个字符串。
    • 示例:"description": "A simple Node.js application"
    4. main
    • 指定项目的入口文件,通常是一个 JavaScript 文件,告诉 Node.js 从哪个文件开始执行。
    • 示例:"main": "index.js"
    5. scripts
    • 定义可通过命令行运行的脚本命令。常用的有 start, test, build 等。

    • 示例:

      "scripts": {
        "start": "node index.js",
        "test": "mocha"
      }
      
    6. dependencies
    • 项目运行时所依赖的包。这里列出的包会在执行 npm install 时自动安装。

    • 示例:

      "dependencies": {
        "express": "^4.17.1"
      }
      
    7. devDependencies
    • 开发时所依赖的包,通常是构建工具、测试框架等,项目在生产环境中不需要这些包。

    • 示例:

      "devDependencies": {
        "webpack": "^5.0.0",
        "babel": "^7.0.0"
      }
      
    8. engines
    • 指定项目支持的 Node.js 版本,确保开发者使用的 Node.js 版本与项目兼容。

    • 示例:

      "engines": {
        "node": ">=14.0.0"
      }
      
    9. author
    • 项目的作者信息。
    • 示例:"author": "Jane Doe <jane@example.com>"
    10. license
    • 项目的许可证信息,表明项目的版权和使用条款。
    • 示例:"license": "MIT"
    示例 package.json
    {
      "name": "my-project",
      "version": "1.0.0",
      "description": "A simple Node.js application",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo "Error: no test specified" && exit 1"
      },
      "dependencies": {
        "express": "^4.17.1"
      },
      "devDependencies": {
        "webpack": "^5.0.0"
      },
      "author": "Jane Doe",
      "license": "MIT"
    }
    

    与pnpm.lock.yaml的联系?

    看到这里,有些朋友可能就会疑惑了,为什么package指定依赖版本范围,pnpm.lock指定特定版本?为什么不能package直接指定依赖版本?这不是脱裤子放屁?别急,听我解释完他们工作的原理,你就清楚了

    首先,package指定一个特定的范围,然后pnpm根据这个范围和内部算法,找到一个最适合你这个项目的版本,例如:a项目中,package里声明这么个依赖:"express": "~4.17.1"意思是范围在4.17.随意,可以是4.17.3,也可以是4.17.6,而pnpm内部有一个算法,计算出指定范围内,4.17.1最适合(这个版本对你的项目兼容性最好,冲突最少,支持的功能最全),就在pnpm-lock锁定版本4.17.1,然后不管什么人把项目从远程拉过来,pnpm install,都是下载4.17.1版本的express,减少了冲突

    如果没有pnpm.lock.yaml

    在前面提到,pnpm内部有算法,会算出范围内最合适的版本,如果没有pnpm-lock,可能会因为环境(开发者的node版本,时间区别)导致pnpm内部算法算出来不同最兼容的版本

    场景

    一个项目依赖express,小A的node版本是16.XX,而小B的node版本是17.XX,pnpm install时,小A项目中pnpm根据内部算法自动下载了4.17.3版本的express,开发完后往远程push,小B把代码拉取下来,执行pnpm install,因为环境不同,pnpm算法自动下载了4.17.1版本的express,版本不同,这时候就非常容易出现版本冲突,导致小B的代码不能正常执行,假如有pnpm-lock.yaml文件,既省去了pnpm内部第二次计算最兼容版本的时间,还不会出现不同开发者依赖不一致的情况。

    总结

    pnpm.lock.yaml实际上是对package 里的依赖版本 起到了补充的作用,减少了冲突发生的概率