豆皮粉儿们,大家好呀。一转眼又陪伴大家来到了不负春光和时行,人间最美的四月天。
作者:羯磨
大家在提交代码时,是否会经常遇到提示package-lock.json有莫名其妙变动的提示?下面就跟这篇文章一起来一探究竟吧。
之前我们项目经常会出现执行npm i后 package-lock.json被更改的问题,但是经常是我们觉得不应该出现被更改的情况而被更改了,看了一下package-locks | npm Docs[1]官方文档,并结合实践分析了一下可能的原因,下面的内容都是针对 npm@7 以下的情况而言的,npm@7 更新了 lockfiles 的版本,具体会在别的文章中介绍
npm@5 以后 npm 会根据 package.json 生成 lockfiles 文件,目的就是为了保证生产和线上编译或者团队开发时大家生成 node_modules tree是一致的,但是即使是这样不同版本的 npm 对于 lockfiles 的处理逻辑是不同的npm install 生成的package-lock.json是什么文件?有什么用? - 知乎[2]。
该版本下 npm 忽略 package.json 的变化,只会根据 lockfiles 下载 node_modules
npm 又变成了会错误的忽略 lockfiles 去下载 node_modules
这版的逻辑我觉得是最自洽的 https://github.com/npm/npm/issues/17979#issuecomment-332701215
If you have a package.json and you run npm i we generate a package-lock.json from it.
If you run npm i against that package.json and package-lock.json, the latter will never be updated, even if the package.json would be happy with newer versions.
If you manually edit your package.json to have different ranges and run npm i and those ranges aren't compatible with your package-lock.json then the latter will be updated with version that are compatible with your package.json. Further runs of npm i will be as with 2 above.
总结起来就是如果我们修改了 package.json 里包的版本,如果新的包的版本和与 lockfiles 里包的版本对照是不符合semver[3]规范的,那么,lockfiles 里对应的 version 就会被更新。
只是简单描述一下 lockfiles 生成的逻辑 我们现在有三个 package,
// package lock-test{ "name": "lock-test", "dependencies": { "A": "^1.0.0" }}// package A{ "name": "A", "version": "1.0.0", "dependencies": { "B": "^1.0.0" }}// package B{ "name": "B", "version": "1.0.0", "dependencies": { "C": "^1.0.0" }}// package C{ "name": "C", "version": "1.0.0"}
在这种情况下 package-lock.json, 会生成类似下面铺平的结构
// package-lock.json{ "name": "lock-test", "version": "1.0.0", "dependencies": { "A": { "version": "1.0.0" }, "B": { "version": "1.0.0" }, "C": { "version": "1.0.0" } }}
简单说会以当前 package.json 包里对应包符合要求的最新版记录在 lockfiles 里,如果后续无论是直接依赖的 A 发版,或者间接依赖的B, C 发版,只要我们不动 package.json, lockfiles 都不会重新生成。
A 发布了新版本 1.1.0,虽然我们 package.json 写的是 ^1.0.0 但是因为 lockfiles 的存在,npm i 并不会自动升级,我们可以手动运行 npm i A@1.1.0
来实现升级。因为 1.1.0 版本与lockfiles 里记录的 A@1.0.0
是不一致的,因此会更新 lockfiles 里的 A 的版本为 1.1.0。
B 发布了新版本 1.0.1, 1.0.2, 1.1.0, 此刻如果我们不做操作是不会自动升级 B 的版本的,但如果此刻 A 发布了 1.1.1,虽然并没有升级 B 的依赖,但是如果我们项目里升级 A@1.1.1
,此时 lockfiles 里会把 B 直接升到 1.1.0 ,因为此刻^1.0.0的最新版本就是 1.1.0。
经过这些操作后 package.json 变成
// package lock-test{ "dependencies": { "A": "^1.1.0" }}
对应的package-lock.json文件
{ "name": "lock-test", "version": "1.0.0", "dependencies": { "A": { "version": "1.1.0" }, "B": { "version": "1.1.0" }, "C": { "version": "1.0.0" } }}
这个时候我们将 B加入我们项目的依赖, B@^1.0.0
,package.json如下
{ "dependencies": { "A": "^1.1.0", "B": "^1.0.0" }}
我们执行这个操作后,lockfiles 并没有被改变,因为现在 lockfiles 里 B@1.1.0
满足 ^1.0.0 的要求
但是如果我们将 B 的版本固定到 2.x 版本, lockfiles 就会发生改变
{ "dependencies": { "A": "^1.1.0", "B": "^2.0.0" }}
因为存在了两个冲突的B版本,package-lock.json文件会变成如下形式
{ "name": "lock-test", "version": "1.0.0", "dependencies": { "A": { "version": "1.1.0", "dependencies": { "B": { "version": "1.1.0" } } }, "B": { "version": "2.0.0" }, "C": { "version": "1.0.0" } }}
因为 B 的版本出现了冲突,npm 使用嵌套描述了这种行为
我们实际开发中并不需要关注这种生成的算法逻辑,我们只需要了解,lockfiles 的生成逻辑是为了能够精准的反映出我们 node_modules 的结构,并保证能够这种结构被还原。
1. 新增或者删除了一些包,但是没有及时 install
我们可以想象出现这样一种场景,a 同学给 package.json 添加了一个 package,但是没有执行 npm install,代码被 push 上去后,b 同学执行 npm i,就会发现 lockfiles 被更改了
2. 挪动了包的位置
将部分包的位置从 dependencies 移动到 devDependencies这种操作,虽然包未变,但是也会影响 lockfiles,会将部分包的 dev 字段设置为 true
3. registry 的影响
经过实际使用发现,如果我们 node_modules 文件夹下的包中下载时的的 registry 与 lockfiles 中包即使 version 相同,但是registry是不同,执行 npm i 时也会修改。
可能还存在其他的原因,但是 lockfiles 是不会无缘无故被更改的,一定是因为 package.json 或者 node_modules 被更改了,因为 正如上面提到的 lockfiles 为了能够精准的反映出我们 node_modules 的结构
目前来看,npm install 是足够可靠的,他能保证根据 lockfiles 还原出开发时的 node_modules,但是为了防止出现刚刚提到的意外情况,除非涉及到对包的调整,其他情况下建议使用 npm ci 来安装依赖,会避免异常的修改 lockfiles,持续集成工具中更推荐是用 npm ci,保证构建环境的准确性,npm i 和 npm ci 的区别可以参考官方文档 npm-install | npm Docs[4],npm-ci | npm Docs[5]
[1]
package-locks | npm Docs: https://docs.npmjs.com/cli/v6/configuring-npm/package-locks
[2]
npm install 生成的package-lock.json是什么文件?有什么用? - 知乎: https://www.zhihu.com/question/62331583/answer/275248129
[3]
semver: https://docs.npmjs.com/cli/v6/using-npm/semver
[4]
npm-install | npm Docs: https://docs.npmjs.com/cli/v7/commands/npm-install
[5]
npm-ci | npm Docs: https://docs.npmjs.com/cli/v6/commands/npm-ci
The End
如果你觉得这篇文章对你有帮助,有启发,我想请你帮我2个小忙:
1、点个「在看」,让更多的人也能看到这篇文章内容;
2、关注公众号「豆皮范儿」,公众号后台回复「加群」 加入我们一起学习;
关注公众号的福利持续更新,公众号后台送学习资料:
1、豆皮范儿后台回复「vis」,还可以获取更多可视化免费学习资料。
2、豆皮范儿后台回复「webgl」,还可以获取webgl免费学习资料。
3、豆皮范儿后台回复「算法」,还可以获取算法的学习资料。
4、豆皮范儿后台回复「招聘」,获取各种内推。