文章编译自: Everything You Wanted To Know About package-lock.json But Were Too Afraid To Ask
你把npm更新到v5.x.x
以后,会出现一种新的自动生成文件 - Package-lock.json
。你如果打开这个文件,会发现它看着像package.json
里面的依赖,不过看着更啰嗦。如果你不去管他,迟早你都会出现依赖的问题,比如依赖的版本没有安装正确。大多数人是直接删除掉package-lock.json
,然后重新运行npm install
来解决问题,这样也行,但是我们还是要搞清楚看这个文件是看什么的。
总结
- 如果你用的是
npm^5.x.x
的版本,它默认就会生成package-lock.json
这个文件 - 你应该使用
package-lock.json
来保证每次安装的依赖都是一样的 - 你应该把
package-lock.json
提交到版本控制库里面 npm^5.1.x
以后,package.json
在某些情况下(文章后面会说)可以优先于package-lock.json
,所以你应该不会那么头疼了。- 不用先删除
package-lock.json
,只需要运行npm install
就可以重新生成package-lock.json
- 如果你的库提供了对外API,应该遵循
semver
规范来变更版本号
背景知识
在你了解package.json
和package-lock.json
前,你需要先了解semver 语义化版本号变更。它是npm依赖管理背后的精髓。你可以点这里了解npm是如何使用它的。总的来说,所谓的语义化版本号变更,就是当你修改了你所维护的第三方库时,告诉那些使用了你的库的项目,你做的是哪种修改。一个版本号分为三个部分: X,Y,Z。X表示主版本号,Y表示次版本号,Z表示补丁更新。当你只是简单的修复了BUG,没有做任何新功能的添加,或者旧功能的修改,就需要更新补丁号(数值加一等等)。当你添加了新的功能,但没有破坏原有的功能,就需要更新次版本号。当你做了重大修改导致新版本不兼容旧的代码时,就需要更新主版本号。
包管理
npm是为了更方便管理依赖。你的项目可能有上百个依赖,每个依赖本身又有上百个依赖。如果让你来记住这些依赖肯定会让你觉得想屎。所以npm诞生了,让你仅仅用几个命令就可以管理大量的依赖。
当你添加了一个依赖时,package.json
里面会加一项条目,包含包的名称,并且使用semver来处理版本号。npm支持版本号中包含通配符。一般来说,npm会安装最新的版本的依赖,并且在版本号前面加上^
符号,比如^1.2.12
。这表示,至少要安装1.2.12
的版本,但更高的版本也可以,只要主版本号也为1就行。因为补丁号和次版本号的变动不会变更已有的API。你应该可以安全地使用任何主版本一致的更高版本依赖。你可以点击这里的版本计算器,了解通配符的含义。
项目共享
把依赖记录在package.json
里面的优点是,只要别人可以访问package.json
,他就可以安装里面所记录的依赖,然后就可以直接运行你的项目了。不过有些时候,这样子会出现一个问题。
比如我们的项目用了express这个框架。我们使用npm init
初始化项目后,使用命令npm install express --save
安装了依赖。在写这篇文章的时候,express的版本号是4.15.4
,所以package.json
里面express的版本号会是^4.15.4
,然后我的电脑上面就安装了版本为4.15.4
的express。然后可能第二天,express的维护者修复了一个BUG,现在最新版本变成了4.15.5
。接着,有个人觉得我们的项目很不错,他想贡献一些代码,于是它clone下来,运行了npm install
。由于最新express版本是4.15.5
,并且主版本号也是一致的,因此他的电脑上安装的版本就是4.15.5
。我们都安装了express,但是版本却不一样。
理论上来说,这两个版本应该是兼容的,但是可能那个修复的BUG影响到了我们所使用的功能,使得在这两个不同的版本上出现不同运行结果。
Package-lock.json
目的
package-lock.json
诞生的目的是为了防止出现我们上述的情况。同一个package.json
却产生了不同的运行结果。package-lock.json
在npm 5时添加进来,所以如果你使用5以上的版本,你就会看到这个文件,除非你手动禁用掉它。
格式
package.json
是一个包含你所有依赖的巨大列表,它包含明确的版本号(没有通配符),依赖的获取地址,一个用于验证完整性和正确性的哈希值,以及这个依赖本身所需要的依赖。我们来看一看express的
所以从此以后npm会根据package-lock.json
里的内容来处理和安装依赖而不是根据package.json
。因为pacakge-lock.json
给每个依赖标明了版本,获取地址和哈希值,使得每次安装都会出现相同的结果。不管你在什么机器上面或什么时候安装。
争议
现在package-lock.json
解决了一个经常遇到的难题,但为什么大家还是想把它禁用掉呢?
在npm 5以前,package.json
是唯一的依赖数据源。package.json
就是权威。npm用户喜欢这个样子并且已经习惯维护package.json
文件。但是,当package-lock.json
文件引入时,它和用户所期待的相反,对package.json
所做的改动并不会反应到package-lock.json
文件上。
例如:依赖A,在package.json
和package-lock.json
的版本号都为1.0.0
。手动把package.json
里A的版本号改为1.1.0
,如果一个用户认为npm install
会依据package.json
来安装依赖,他们期望安装的版本是1.1.0
,结果却安装了1.0.0
。尽管package.json
里面写的是1.1.0
只因为用户不知道为什么他们的依赖没有按照预想当中样子安装,他们就宁愿禁用package-lock.json
文件。
这个预想和现实中的冲突引发了一个特别有趣的issue。一些人支持package.json
是依赖的权威来源,一些人支持package-lock.json
。这个争议的解决方案在PR #17508。npm的维护者做了一个改动:当package.json
文件有发生变更时,使用它作为依赖的来源。现在,不管在任何情况下,依赖安装都可以满足用户的预期了。这个改动发布于npmv5.1.0
。