关于package-lock.json

前言

上篇文章我们了解了package.json,一般与它同时出现的还有一个package-lock.json,这两者又有什么关系呢?下面一起来了解吧。

介绍

package-lock.json 它会在 npm 更改 node_modules 目录树 或者 package.json 时自动生成的 ,它准确的描述了当前项目npm包的依赖树,并且在随后的安装中会根据 package-lock.json 来安装,保证是相同的一个依赖树,不考虑这个过程中是否有某个依赖有小版本的更新。

为什么需要package-lock.json

相信跟多人跟我一样会有一个疑问:为什么有了package.json还需要package-lock.json?实际上两者并不是同一时期提出来的,package-lock.json是在npm5之后才提出来的,从上面MDN的介绍来看,它的出现主要是为了解决依赖的版本管理问题。

npm install 执行后,会生成一个node_modules 树,在理想情况下, 希望对于同一个 package.json 总是生成完全相同 node_modules 树。在某些情况下,确实如此。但在多数情况下,npm 无法做到这一点。有以下两个原因:

  • 某些依赖项自上次安装以来,可能已发布了新版本 。比如:A 包在团队中第一个人安装的时候是 1.0.5 版本,package.json 中的配置项为 A: '^1.0.5',团队中第二个人把代码拉下来的时候,A 包的版本已经升级成了 1.0.8,根据 package.json 中的 semver-range version 规范,此时第二个人 npm install 后 A 的版本为 1.0.8,可能会造成因为依赖版本不同而导致的 bug
  • 针对上面的问题,可能有的小伙伴会觉得把 A 的版本号固定为 A: '1.0.5' 不就可以了吗?但是这样的做法其实并没有解决问题, 比如 A 的某个依赖在第一个人下载的时候是 2.1.3 版本,但是第二个人下载的时候已经升级到了 2.2.5 版本,此时生成的 node_modules 树依旧不完全相同 ,固定版本只是固定来自身的版本,依赖的版本无法固定

关于依赖的版本

我们可以先来了解依赖的版本

perl 复制代码
{
   "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
   }
}

比如我们常见的依赖版本一般长这样,它一般由三部分组成:major.minor.patch,依次为主版本号、次版本号、修补版本号。

  • 主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改,则内容将无法正常工作。
  • 次要版本的更改表示不会破坏任何内容的新功能。
  • 修补版本的更改表示不会破坏任何内容的错误修复。

比如上面我们看到的^10.0.0,主版本号为10、次版本号为0、修补版本号为0,那^表示什么呢?

版本号指定标识符

这个符号其实是用来指定版本范围的,与之对应的有以下符号:

  • ^ 会匹配最新的大版本依赖包,比如 ^1.2.3 会匹配所有 >=1.1.2 <2.0.0 的版本,包括 1.3.0,但是不包括 2.0.0
  • ~ 会匹配最近的小版本依赖包,比如 ~1.2.3 会匹配所有 >=1.1.2 <1.2.0 的版本,但是不包括 1.3.0
  • * 安装最新版本的依赖包,比如 *1.2.3 会匹配 x.x.x
  • 无符号时,比如 1.2.3,那就是将要使用的确切版本,总是会下载这个版本的依赖包

认识package-lock.json

这个文件看起来比package.json又大有复杂,动不动就是上万行代码。

我们可以只安装某一个依赖看看它内部长啥样,比如axios

css 复制代码
{
  "name": "demo",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "asynckit": {
      "version": "0.4.0",
      "resolved": "https://mirrors.tencent.com/npm/asynckit/-/asynckit-0.4.0.tgz",
      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
    },
    "axios": {
      "version": "1.4.0",
      "resolved": "https://mirrors.tencent.com/npm/axios/-/axios-1.4.0.tgz",
      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
      "requires": {
        "follow-redirects": "^1.15.0",
        "form-data": "^4.0.0",
        "proxy-from-env": "^1.1.0"
      }
    },
    "combined-stream": {
      "version": "1.0.8",
      "resolved": "https://mirrors.tencent.com/npm/combined-stream/-/combined-stream-1.0.8.tgz",
      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
      "requires": {
        "delayed-stream": "~1.0.0"
      }
    },
    "delayed-stream": {
      "version": "1.0.0",
      "resolved": "https://mirrors.tencent.com/npm/delayed-stream/-/delayed-stream-1.0.0.tgz",
      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
    },
    "follow-redirects": {
      "version": "1.15.2",
      "resolved": "https://mirrors.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.2.tgz",
      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
    },
    "form-data": {
      "version": "4.0.0",
      "resolved": "https://mirrors.tencent.com/npm/form-data/-/form-data-4.0.0.tgz",
      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
      "requires": {
        "asynckit": "^0.4.0",
        "combined-stream": "^1.0.8",
        "mime-types": "^2.1.12"
      }
    },
    "mime-db": {
      "version": "1.52.0",
      "resolved": "https://mirrors.tencent.com/npm/mime-db/-/mime-db-1.52.0.tgz",
      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
    },
    "mime-types": {
      "version": "2.1.35",
      "resolved": "https://mirrors.tencent.com/npm/mime-types/-/mime-types-2.1.35.tgz",
      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
      "requires": {
        "mime-db": "1.52.0"
      }
    },
    "proxy-from-env": {
      "version": "1.1.0",
      "resolved": "https://mirrors.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
    }
  }
}

从这里我们可以发现,它的dependenciespackage.json不一样,它除了axios之外还包含了一些其它的依赖,实际上这些其它的依赖都是axios的依赖或者是它依赖的依赖...

从上面的介绍中我们也能知道,它实际上描述的是当前项目的依赖树

它有一些与package.json文件中不同的属性,比如:

lockfileVersion

一个整数版本,从1开始,该文档的版本号

resolved

依赖的安装地址,其实就是一个包下载地址

intergrity

表示解压的完整性 Hash 值

dev

表示该模块是否为顶级模块的开发依赖或者是一个的传递依赖关系

requires

依赖包所需要的所有依赖项,对应依赖包 package.jsondependencices 中的依赖项

npm install策略

当我们每次使用npm install进行依赖安装的时候,它到底是按照什么规则去帮我们下载依赖的呢?

这里其实有好几个版本,但我们只需要了解最新版本就行了。

先看有无lock文件:

如果有,则对比package.json和package-lock.json

  • 如果package-lock.json里包版本号符合package.json要求,则直接获取包信息(如果是从远程拉取,则按照package-lock.json,否则以实际缓存的为准),构建依赖树,(注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包);那么接下来就看.npmrc里有没有缓存,如果有缓存文件,则从缓存文件中拉取内容,否则从远程拉取;并更改package-lock。json版本号
  • 如果版本号不符合要求,则直接从远程拉取,并更新package-lock.json中的版本号

如果没有,则:

  • 根据package.json构建依赖树(注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包)
  • 如果缓存中(.npmrc)有,则优先从缓存中读取,否则从远程读取;注意:(如果是从远程拉取,则按照package.json,否则以实际缓存的为准)

需要注意的是,在使用cnpm install时候,并不会生成 package-lock.json 文件,也不会根据 package-lock.json 来安装依赖,它只会根据 package.json 来安装依赖

场景一

json 复制代码
// package.json
"dependencies": {
  "vue": "^2.0.0"
}
​
// package-lock.json
"dependencies": {
  "vue": {
      "version": "2.7.14",
      "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.7.14.tgz",
      "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==",
      "requires": {
        "@vue/compiler-sfc": "2.7.14",
        "csstype": "^3.1.0"
      }
    }
}
​

这种情况下package-lock.json指定的2.7.14^2.0.0指定的范围内,npm install会安装vue2.7.14版本。

场景二

json 复制代码
// package.json
"dependencies": {
  "vue": "^2.2.0"
}
​
// package-lock.json
"dependencies": {
  "vue": {
    "version": "2.1.0",
    "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.1.0.tgz",
    "integrity": "sha1-KTuj76rKhGqmvL+sRc+FJMxZfj0="
  }
}
​

这种情况下package-lock.json指定的2.1.0不在^2.2.0指定的范围内,npm install会按照^2.2.0的规则去安装最新的2.7.14版本,并且将package-lock.json的版本更新为2.7.14

现在应该能够理解package.json文件是如何做到对依赖进行版本锁定的吧,我们一般在安装依赖时如果不指定版本,那么安装的版本号并不是固定的而是一个最优版本 ,最优版本会在版本前多了一个^或者~符号

json 复制代码
"dependencies": {
    "vue": "^2.0.0"
  }

但我们的lock文件中肯定是会指定一个固定版本进行安装的,一般是改依赖的符合版本范围的最新版本

json 复制代码
"dependencies": {
  "vue": {
    "version": "2.1.0",
    "resolved": "https://mirrors.tencent.com/npm/vue/-/vue-2.1.0.tgz",
    "integrity": "sha1-KTuj76rKhGqmvL+sRc+FJMxZfj0="
  }
}

至于为什么不直接在package.json中将版本锁定,那是因为你只能指定你安装的依赖的版本,但不能指定你依赖的依赖的版本

package-lock.json什么时候会变?

开发过程中是不是经常遇到这个文件冲突的,自己明明没改这个文件为啥会冲突?那是因为我们的一些操作会影响到该文件的内容,比如:

  • package-lock.jsonnpm install的时候会自动生成
  • 当我们修改依赖位置,比如将部分依赖从开发依赖改生产依赖,虽然整体上的依赖并未改变,但是也会影响 package-lock.json中依赖的 dev 字段
  • 如果我们切换npm镜像时,执行 npm install 时也会修改 package-lock.json,因为它是会记录我们的依赖包地址的(resolved)
  • 当我们使用npm install添加或npm uninstall移除包的时候,也会修改 package-lock.json
  • 当我们更新某个包的版本的时候,也会修改 package-lock.json

package-lock.json需要提交到仓库吗?

npm 官网建议:把 package-lock.json 一起提交到代码库中,不要 ignore。但是在执行 npm publish 的时候,它会被忽略而不会发布出去。

如何查看依赖安装的版本?

上面我们已经了解到,package.json中保存的依赖版本一般不是一个具体版本,而是一个带有^~的最优版本,那我们怎么才能知道当前项目依赖安装的具体版本呢?

  • 查看package-lock.json文件,这里保存的是依赖的具体版本
  • node_modules文件夹中找到对应依赖的package.json文件,里面的version字段就是该依赖的版本
  • 使用npm list --depth 0查看项目所有的依赖版本

如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,关注 前端南玖 第一时间获取最新文章~

相关推荐
辻戋2 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保2 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun3 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp3 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.4 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl6 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js