[toc]
来了新同事,拉同一个项目到本地安装依赖之后跑不起来,但是其他三台电脑运行着都没问题。接下来就是逐步定位问题,首先排除了代码问题,因为最新代码在其他同事不同系统的电脑上都没正常运行,进过百度/谷歌/github issue搜索报错、反复重新拉项目、重启电脑、重装环境、重装系统等一天的的操作之后,终于定位到大概是依赖包版本更新的问题。。
项目里一共有60+依赖,主要的几个依赖都是手动锁死版本的,但有个lozad没有锁死,而且前段时间应该是发布了次版本更新,跟nuxt的兼容有问题所以报了错。跟运行正常项目的node_moduels中lozad版本对比,改了之后果然项目就跑起来了。
npm包管理原理
在思考解决方案前,首先了解下npm包管理及依赖版本管理的原理。这些都是通过package.json
文件实现的
当你使用npm安装一个包(并保存它)或者更新一个包的时候,package.json
里就自动添加了一条信息,包括包名和其版本。npm默认安装最新版本,然后在其版本号之前添加一个^符号。比如^1.2.12,它表明最低应使用1.2.12版本。并且在这之上,拥有相同大版本号的任何版本都是OK的。毕竟小版本和bugfix版本不会对使用造成任何影响,所以用任何相同大版本的更高级版本都很安全。
- 符号
^
:表示主版本固定的情况下,可更新最新版。例如: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版本。
举例:
"^1.2.3": 大于等于 1.2.3 且小于 2.0.0版本
"^0.3.4": 大于等于 0.3.4 且小于 0.4.0版本
"^0.0.6": 大于等于 0.0.6 且小于 0.0.7版本
版本依赖为什么需要锁定
没有版本锁定的情况下,在执行每次npm i
的时候,对应的版本前都有个 ^
符号。也就是未固定版本的依赖如果有了次版本更新或者修订版本更新,会自动安装对应的最新版。
在这种情况下,你再次install时安装的包的版本可能与前次不一样,具体的,你可以到package-lock.json中查看实际的包版本。
例如:A新建了一个项目,生成了上面这份package.json文件,但A安装依赖的时间比较早,此时packageA的最新版本是2.1.0,该版本与代码兼容,没有出现bug。后来B克隆了A的项目,在安装依赖时packageA的最新版本是2.2.0,那么根据语义npm会去安装2.2.0的版本,但2.2.0版本的API可能发生了改动,导致代码出现bug。
这就是package.json会带来的问题,同一份package.json在不同的时间和环境下安装会产生不同的结果。
理论上这个问题是不应该出现的,因为npm作为开源世界的一部分,也遵循一个发布原则:相同大版本号下的新版本应该兼容旧版本。即2.1.0升级到2.2.0时API不应该发生变化。但很多开源库的开发者并没有严格遵守这个发布原则,导致了上面的这个问题。
为了在不同的环境下生成相同的node_modules,引入版本依赖锁定就尤为必要了。
npm5.0之前可以通过npmshrinkwrap
实现。通过运行 npm shrinkwrap
,会在当前目录下生成一个 npm-shrinkwrap.json
文件,里面包含了通过当前 node_modules
计算出的模块的依赖树及版本。只要目录下有 npm-shrinkwrap.json
,则运行 npm install
的时候会优先使用 npm-shrinkwrap.json
进行安装,没有则使用 package.json
进行安装。
在npm5.0之后,npm自带了package-lock.json
文件,通过npm安装依赖,每当node_modules
目录或者package.json
发生变化时就会生成或者更新这个文件。不同版本有有些不同:
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
文件,这个文件相当于本次install的一个快照,它不仅记录了package.json
指明的直接依赖的版本,也记录了间接依赖的版本。
如果package.json
的semver-range version和package-lock.json
中版本兼容(package-lock.json
版本在package.json
指定的版本范围内),即使此时package.json
中有新的版本,执行npm i也还是会根据package-lock.json
下载 - 实践场景1。
如果手动修改了package.json
的version ranges,且和package-lock.json
中版本不兼容,那么执行npm i时package-lock.json
将会更新到兼容package.json
的版本 - 实践场景2。
如果需要更新依赖依赖包版本,需要手动修改package.json
中对应的版本或者指定依赖的版本号安装:npm i xxx@x.x.x
。
更换/管理npm源
首先要说的是,很多同学可能习惯使用cnpm,因为安装速度确实比npm快不少,但在版本依赖锁定方案中,最基础的一条就是:不要使用cnpm,因为cnpm,是不支持依赖版本锁定的。也即是说,无论你的项目中有package-lock.json
、npm-shrinkwrap.json
还是yarn-lock.json
文件,执行cnpm i
安装依赖的时候他们都只是摆设,都只会根据package.json
文件进行安装。所以通过cnpm安装依赖是不能避免上面问题的。而且有很多网友反馈cnpm会有依赖包丢失的问题。
但是使用npm
避不开的一个问题就是安装速度,实在太慢了。这里我们可以通过手动更换npm源和nrm的方式实现使用npm命令的同时,依然享受cnpm的安装速度。
手动更换npm源
设置npm源: npm config set registry [url]
查看确认: npm config get registry
使用nrm
安装nrm
npm i nrm -g
查看可选的源
nrm ls
其中,带*的是当前使用的源,上面的输出表明当前源是官方源。
切换到某个源:nrm use xx
例如切换到淘宝源:nrm use taobao
增加源(添加企业内部的私有源或者其他源):nrm add [registryName] [url]
删除源:nrm del <registryName>
测试某个源的相应时间:nrm test taobao
依赖版本锁定方案
大概有这么几条方案:
package.json
中固定版本npm
+package-lock.json
npm
+npm-shrinkwrap.json
yarn
+yarn-lock.json
package.json
中固定版本
最直接的,可以在package.json
中写入固定版本号,也就是去掉版本号前面的~
或者^
,或者安装的时候加上--save-exact
参数。但这样只能锁定最外一层的依赖,也就是这个依赖本身的其他依赖版本是不受控制的。所以不太推荐。
npm
+package-lock.json
第一次npm i
的时候会根据当前node_modules
目录生成一个固定版本号的package-lock.json
文件,后面如果安装新增的依赖,会自动更新这个文件。但如果需要更新当前某个依赖的版本号并锁定到package-lock.josn
中,需要手动修改package.json
中对应的版本或者指定依赖的版本号安装:npm i xxx@x.x.x
。
npm
+npm-shrinkwrap.json
这种方式锁定版本,每次依赖有新增或者版本更新之后,要手动智行npm shrinkwrap
来生成或者更新版本锁定文件。
yarn
+yarn-lock.json
yarn-lock.json
与package-lock.josn
原理类似,习惯用yarn
命令的可以采取这种方式。
注:
如果项目中同时存在package-lock.json
和npm-shrinkwrap.json
,npm5 只会更新它,而不会生成 package-lock.json。
yarn 的锁定版本文件叫 yarn.lock,目前发布平台是支持的,不过最好保证项目中只有一个版本锁定文件,package-lock.json、npm-shrinkwrap.json 或者 yarn.lock 二选一,防止出现安装结果和预想不一致的情况。
npm
和cnpm
的区别
cnpm i
不受package-lock.json
影响,只会根据package.json
进行下载。cnpm i xxx@xxx
不会更新到package-lock.json
中去。npm i xxx@xxx
会跟新到package-lock.json
中去
阿里云限量优惠券免费领:云服务器ECS、云数据库MySQL、对象存储OSS等
参考
npm5.0新增package-lock.json文件挖的坑
前端核心工具:yarn、npm、cnpm三者如何优雅的在一起使用 ?
npm的package.json和package-lock.json更新策略
本文由博客群发一文多发等运营工具平台 OpenWrite 发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。