npm依赖版本锁定详解

npm中有一个package-lock.json的文件,即npm依赖锁文件,用来描述npm依赖生成的确切树,这样不管你的依赖有何种更新,都会按照这个确切树来安装使用。

不同的包管理工具对应不同的锁文件:

● npm => package-lock.json

● yarn => yarn.lock

● pnpm => pnpm-lock.yaml

整体上大同小异,本文主要以npm为例。

一、semver 版本控制规范

在软件管理的领域里存在着被称作"依赖地狱"的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

为了解决依赖包的版本混乱问题,制定了这个 semver 规范。

版本格式:【主版本号.次版本号.修订号】,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的 API 修改,(不兼容更新)
  2. 次版本号:当你做了向下兼容的功能性新增,(功能新增)
  3. 修订号:当你做了向下兼容的问题修正。(bug修复)
    先行版本号及版本编译信息可以加到"主版本号.次版本号.修订号"的后面,作为延伸。(例如beta、test版本)

(更详细内容参考官网文档:https://semver.org/lang/zh-CN/)

二、安装依赖时的版本规则

npm依赖规则中,还有 >、>=、<、<=、x、*、-、||、~、^ 等符号。通过在版本号前面加上这些符号,可以有效的限制依赖的版本。

  • version:必须依赖某个具体的版本。如:2.5.2,表示必须安装2.5.2版本。
  • >version:必须大于某个版本。
  • >=version:大于或等于某个版本。
  • <version:必须小于某个版本。
  • <=version:小于或等于某个版本
  • x-range:x的位置可以为任意版本。
  • *-range:任意版本。""也表示任意版本。
  • version1 - version2:大于等于version1,小于等于version2。
  • range1 || range2:满足range1或者满足range2,可以有多个范围。
  • ~version:大概匹配某个版本。
    • 如果次版本号(Y)指定了,那么次版本号(Y)不变,而修订号(Z)任意。
    • 如果次版本号(Y)和修订号(Z)未指定,那么次版本号(Y)和修订号(Z)任意。
  • ^version:向上兼容某个版本。
    ○ 从版本号最左侧开始,到首个非零数字,这些位置固定,其余位置任意。如果缺少某个位置,则这个位置可以任意。
json 复制代码
"~2.5.2" // 表示 >=2.5.2 且 <2.6.0
"~2.5" // 表示 >=2.5.0 且 <2.6.0
"~2" // 表示 >=2.0.0 且 <3.0.0
json 复制代码
"^2.5.2" // 表示 >=2.5.2 且 <3.0.0
"^0.1.3" // 表示 >=0.1.3 且 <0.2.0
"^0.0" // 表示 >=0.0.0 且 <0.1.0

使用npm安装依赖时在 package.json 里生成的默认是 ^version 形式,安装时会安装最新的依赖。

  • 这意味着,在多人协作时,如果一个人安装了某个版本,另一个人在安装时这个依赖更新了此版本号,就会安装另一个版本,不同的版本会有差异,可能就会出现问题。
  • 另外在本地构建和服务器构建时可能也会有类似的差异导致问题。

三、依赖锁定

如果所有的node包都严格符合语义化版本(semver)管理的规则,那么npm的最优版本号就能保证所下载的依赖包一定是与代码兼容的。

由于无法保证这一前提,如果想要保证他人下载的依赖包与我们的代码绝对兼容,就需要锁定项目中依赖包的版本号。

1. 写死版本号锁定

最简单的方法,就是指定具体版本号,可以将package.json中版本号开头的 ^ 和 ~ 等标记去掉。

后续安装新的依赖包时,则使用 npm i --save-exact <package_name> 或者 npm i --save <package_name>@1.2.3(指定依赖的具体版本),这样package.json中就不会出现最优版本的标记。

  • 缺陷:无法锁定次级依赖的版本号,即依赖包的依赖包。

2. 锁文件

由于在重新安装依赖时,依赖树模块的版本存在着不确定性,为了解决这个问题,npm提供了 npm-shrinkwrap.json 或 package-lock.json 文件,这两种文件被称为包锁或锁文件。

1)npm shrinkwrap

在npm 5版本以前(不包括npm 5),可以通过执行 npm shrinkwrap 命令,创建一个新的或覆盖已有的 npm-shrinkwrap.json 文件。该文件记录了目前所有依赖包(及更底层依赖包)的版本信息。

当再次运行npm install命令重新安装依赖时,npm首先会找npm-shrinkwrap.json文件,依照其中的信息来准确地安装每一个依赖包,只有当这个文件不存在时,npm才会使用package.json。

  • 每次更新package.json或者node_modules时,如: npm install新包、npm update、npm uninstall等操作,为了保证所有开发人员的资源一致,还是要手动运行npm shrinkwrap更新npm-shrinkwrap.json文件。
  • npm shrinkwrap计算时是根据当前依赖安装的目录结构生成的,如果不能保证package.json文件定义的依赖与node_modules下已安装的依赖是匹配、无冗余的,建议在执行npm shrinkwrap命令前清理依赖并重新安装(rm-rf node_modules&&npm install)或精简依赖(npm prune)。
2)package-lock.json

在npm 5以后,运行npm intall会自动生成一个新文件package-lock.json,其内容跟上面提到的npm-shrinkwrap.json基本一样,在修改pacakge.json或者node_modules时会自动产生或更新它。

当项目中已存在package-lock.json文件,再安装项目依赖时,将以该文件为主进行解析安装指定版本的依赖包,而不是使用package.json来解析和安装。

因为package-lock.json为每个模块及其每个依赖项都指定了版本、位置和完整性哈希,所以它每次创建的安装都是相同的。

  • cnpm并不支持package-lock。 使用cnpm install时,并不会生成package-lock.json文件。即使项目中已有package-lock.json文件,执行cnpm install命令,cnpm 也不会识别,仍会根据package.json安装依赖。因此,尽量避免直接使用cnpm install安装项目的依赖。
3)区别和联系
  • package-lock.json是npm 5的新特性,且不向下兼容,因此如果npm版本是5以下,还是使用npm shrinkwrap命令。
  • package-lock.json和npm-shrinkwrap.json这两个文件的优先级都比package.json高。
    • 同一个项目里,如果不存在这两个文件,在运行npm install或者初始化项目npm init时,会自动生成一个package-lock.json(npm 5及以上)。
    • 如果这两个文件都存在,安装依赖则是依据npm-shrinkwrap.json,而忽略package-lock.json。
    • 如果项目里不存在package-lock.json,运行命令npm shrinkwrap后,会创建一个npm-shrinkwrap.json文件
    • 如果存在package-lock.json,则会将其重命名为npm-shrinkwrap.json。
  • npm-shrinkwrap.json只有在运行npm shrinkwrap命令时才会创建或更新;而package-lock.json会在修改pacakge.json或者node_modules时自动产生或更新。

四、要不要锁

一直以来这都是个有争议的问题。

1. 两种锁的方式:

  • 一是在package.json里对个别依赖写死版本号锁定。
  • 二是通过锁文件对所有依赖都锁定。

2. 个人建议:

  • 当项目的维护可能陷入停滞或者很少更新时,通过锁文件来锁住依赖,保证项目即使过了很长时间依然能稳定跑起来。
  • 当项目和依赖有专门的维护团队来长期维护时,不锁依赖,一般这种依赖的版本更新会比较规范,有问题也能及时发现和修复。
    ○ 如果是个别依赖的维护不稳定,这部分依赖可以通过package.json写死版本号来锁定。

其他情况就需要综合考量了,利弊都有,没有绝对的说法。

相关推荐
小白求学16 分钟前
CSS响应式布局
前端·css
Minyy1121 分钟前
小程序项目实践(一)--项目的初始化以及前期的准备工作
开发语言·前端·git·小程序·gitee·uni-app
谢尔登1 小时前
【React】如何对组件加载进行优化
前端·react.js·前端框架
华实coding1 小时前
ajax实现添加数据
前端·ajax·okhttp
黄毛火烧雪下1 小时前
React 为什么 “虚拟 DOM 顶部有很多 provider“?
前端·javascript·react.js
Meowmow1 小时前
React学习01 jsx、组件与组件的三大属性
前端·学习·react.js
岁聿云暮1 小时前
机械臂之贝塞尔曲线的应用
前端·vue.js
霸气小男1 小时前
react 封装防抖
前端·javascript·react.js
張三同学1 小时前
仿IOS桌面悬浮球(支持拖拽、自动吸附、自动改变透明度与点击、兼容PC端与移动端)
前端·javascript
视频砖家2 小时前
BootStrap组件class根据不同设备设定显示与隐藏
前端·bootstrap·html