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写死版本号来锁定。

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

相关推荐
LUwantAC5 分钟前
CSS(四)display和float
前端·css
cwtlw9 分钟前
CSS学习记录20
前端·css·笔记·学习
界面开发小八哥14 分钟前
「Java EE开发指南」如何用MyEclipse构建一个Web项目?(一)
java·前端·ide·java-ee·myeclipse
米奇妙妙wuu28 分钟前
react使用sse流实现chat大模型问答,补充css样式
前端·css·react.js
傻小胖33 分钟前
React 生命周期完整指南
前端·react.js
梦境之冢1 小时前
axios 常见的content-type、responseType有哪些?
前端·javascript·http
racerun1 小时前
vue VueResource & axios
前端·javascript·vue.js
m0_548514772 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
AndrewPerfect2 小时前
xss csrf怎么预防?
前端·xss·csrf
Calm5502 小时前
Vue3:uv-upload图片上传
前端·vue.js