npm 包管理工具及package-lock.json 的更新策略
语义化版本
在一探package-lock.json
究竟之前,你必须要理解semver。它是npm
背后的小小功臣。你可以从这里了解到npm
是如何使用它的。概括来讲,假若你在开发一个可供其它应用使用的应用,你必须说明每次升级变更会对第三方使用产生哪些影响。这就是语义化版本想要传达的。一个版本有三部分:X, Y, Z,分别指代大版本,小版本,与查缺补漏版本。比如1.2.3,那么就是大版本1,小版本2,bugfix版本3。bugfix版本不会影响任何功能,小版本变更往往是增加新功能,也不会影响使用。而大版本变更往往会带来使用层面不兼容的情况,需要再做调整。(想想webpack每次升级的时候!)
NPM包管理
正是为了让包管理变简单,npm
出现了。一个项目可能有上百个依赖,每个依赖又有上百个依赖。为了你不陷入依赖地狱,只需简单几行命令,npm
就可以安装并管理这些依赖,大大节省了时间。
- 符号
^
:表示安装不低于该版本的应用,但是大版本号需相同,例如:vuex: "^3.1.3"
,3.1.3
及其以上的3.x.x
都是满足的。 - 符号
~
:表示安装不低于该版本的应用,但是大版本号和小版本号需相同,例如:vuex: "^3.1.3"
,3.1.3
及其以上的3.1.x
都是满足的。 - 无符号:无符号表示固定版本号,例如:
vuex: "3.1.3"
,此时一定是安装3.1.3
版本。
为什么需要package-lock.json
npm install 的输入是 package.json,它的输出是一棵 node_modules 树。理想情况下,npm install 应该像纯函数一样工作,对于同一个 package.json 总是生成完全相同的 node_modules 树。在某些情况下,确实如此。但在其他很多情况中,npm 无法做到这一点。有以下原因:
- 不同版本的 npm 的安装算法不同。
- 某些依赖项自上次安装以来,可能已发布了新版本,因此将根据 package.json 中的 semver-range version 更新依赖。
- 某个依赖项的依赖项可能已发布新版本,即使您使用了固定依赖项说明符(1.2.3 而不是 ^1.2.3),它也会更新。
为了在不同的环境下生成相同的 node_modules,npm 使用 package-lock.json 或 npm-shrinkwrap.json。这两个文件都被称为 lockfiles。无论何时运行 npm install,npm 都会生成或更新 lockfiles。以下只讨论其中的 package-lock.json。
在.npmrc文件中设置package-lock = true,会在install 的时候产生package-lock.json的文件
不同 npm 版本下 npm i 的规则
-
npm 5.0.x 版本:不管 package.json 中依赖是否有更新,npm i 都会根据 package-lock.json 下载。针对这种安装策略,有人提出了这个 issue - #16866 ,然后就演变成了 5.1.0 版本后的规则。
-
5.1.0 版本后:当 package.json 中的依赖项有新版本时,npm install 会无视 package-lock.json 去下载新版本的依赖项并且更新 package-lock.json。针对这种安装策略,又有人提出了一个 issue - #17979 ,参考 npm 贡献者 iarna 的评论,得出 5.4.2 版本后的规则。
-
5.4.2 版本后:
-
如果只有一个 package.json 文件,运行
npm i
会根据它生成一个 package-lock.json 文件。 -
如果 package.json 的 semver-range version 和 package-lock.json 中版本兼容,即使此时 package.json 中有新的版本,执行
npm i
也还是会根据 package-lock.json 下载 - 实践场景1。 -
如果手动修改了 package.json 的 version ranges,且和 package-lock.json 中版本不兼容,那么执行
npm i
时 package-lock.json 将会更新到兼容 package.json 的版本 - 实践场景2。
-
实践
npm 版本:6.4.1
场景1
-
假设刚从远程仓库克隆一个项目,此时本地 node_modules 还不存在,package.json 和 package-lock.json 如下。已知 superagent 3.x.x 的最新版本是 3.8.3,那么运行 npm install 是根据 package-lock.json 中指定的版本 3.5.1 去下载还是根据 package.json 去下载最新的 3.x.x ?
// package.json "dependencies": { "superagent": "^3.5.1" } // package-lock.json { "superagent": { "version": "3.5.1", "resolved": "https://npm.garenanow.com/superagent/-/superagent-3.5.1.tgz", "integrity": "sha1-Ck+u/aM2d3d4iDR917TSH0EMhxs=", "requires": { "component-emitter": "^1.2.0", "cookiejar": "^2.0.6", "debug": "^2.2.0", "extend": "^3.0.0", "form-data": "^2.1.1", "formidable": "^1.1.1", "methods": "^1.1.1", "mime": "^1.3.4", "qs": "^6.1.0", "readable-stream": "^2.0.5" } }, } 复制代码
结论:下载的是
3.5.1
。此时 package.json 和 package-lock.json 同时存在,package.json 的 version 是^3.5.1
,package-lock.json 的 version 是3.5.1
,并且当前 node_modules 中下载的也是3.5.1
。
场景2
- 接着场景1,然后手动修改 package.json 中 superagent 的版本为
^5.1.0
,再执行 npm i,发现不管有没有删除已有的 node_modules,package-lock.json 中 superagent 的版本都变成了5.1.0
,node_modules 中的也变成了5.1.0
。
链接:https://juejin.cn/post/6844903903193104398