关于npm版本管理的坑

版本号规则, node version
package.json, package-lock.json
nvm, npm, yarn

npm 版本号规则

参考:Semantic Versioning

所有的 npm 包都采用以下规则定义版本号(称为语义化版本):

所有的版本号都用三个数字进行标记:x.y.z

  • 第一位 x 代表主版本(major version):更新API(不兼容)
  • 第二位 y 代表副版本(minor version):增加功能(向下兼容)
  • 第三位 z 代表补丁版本(patch version):修复 bug(向下兼容)

那么,我们在 package.json 中常见的 ^x.y.z~x.y.z 之类的规则就容易解释了:

  • ^^0.13.0 代表同意 patch 和 minor 版本的更新,也就是当 npm update 时该版本可以更新到 0.13.10.14.0
  • ~^0.13.0 代表同意 patch 版本的更新,也就是当 npm update 时该版本可以更新到 0.13.1,但不会更新到 0.14.0
  • > / >= / < / <= / =:语义较为直接,不赘述
  • -:可以指定接受的版本范围,如 2.1.0-2.6.2
  • ||:可以给规则集取并,如 < 2.1 || > 2.6
  • 无符号标记:指定某个确定版本
  • latest:最新的可用版本

package.json、package-lock.json、yarn

经常会出现按照同一个 package.json 在不同人的机器上 npm install 出的环境不一致的问题,这其中一部分原因就是由于上面所描述的版本号规则,导致不同时间下载的包版本可能不同。

那么,在 package.json 中把所有包的版本都严格指定就好了?

很遗憾,还是不行。

因为这些包自己的 package.json 中又引用了别的包,还是无法保证第二层第三层…包的版本一致。

于是 npm 引入了 package-lock.json 这个文件,正如它的名字“锁”,它严格锁定了所有包的版本号(包括第二层第三层…的包),甚至还指定了每个包的下载地址。如果项目中存在 package-lock.jsonnpm install 会按照它而不是 package.json 进行安装。

所以一个比较好的实践是,多人协作时把 package-lock.json 也放到 git 上。

再之后,yarn 出现了,yarn 用一个 yarn.lock 文件做了和上面 package-lock.json 同样的锁定版本工作,而且这个文件是默认创建更新的,也就解决了需要维护 package.jsonpackage-lock.json 两个文件的问题。

yarn 还有一堆优点,这里就不赘述了,总之新项目用 yarn 肯定要比用 npm 方便,但对于一些没有用 yarn 的老项目,就要注意上面的版本管理问题了。

node-sass 经常产生的 node 版本问题

对于老项目,还有个很痛的地方,node-sass,特别是 node-sass 在 windows 上安装。需要下载的东西多且要翻墙、需要 gyp、需要 windows-build-tools……中途万一哪里卡住想要推倒重来,却会发现 npm uninstall 不能完全卸载 windows-build-tools,还要手工卸载手工重装,python27 还好,寻找老版本的 visual studio c++ 简直是噩梦……

而当你搞定了以上一切,以为终于能顺利安装时,又遇到了一堆堆的 Build failed with error code: 1 ……

其中一个问题坑了我一天时间,最终在 [Unsupported] Installing node-sass 4.11.0 with Node 12 找到了答案。

node-sass 4.11.0,不兼容 node 12!

升 node-sass 版本,别的开发者可能会受影响;降 node 版本,强迫症浑身难受……

然后我就找到了下面这个神器:

nvm

没错,Node Version Manager,管理 node 版本的工具!windows 版的地址:nvm-windows

按照说明下载安装就行了,需要注意的几个小地方:

  • 安装前需要删除本地已经安装的 node 和 npm(如何彻底卸载请自行 google),如果有一些 npm config 可以先备份 npmrc
  • 如果你有多个版本的 node,需要对每个版本都重新安装全局包们,它们不会在不同 node 版本中共享
  • 勤用 node -v 检查版本,如果第一条没搞好可能 nvm 根本不会起效…

于是我把 node 版本切到 11 再去跑老项目,终于一路通畅无阻啦!

一点点反思:package.json 中其实可以指定 node 版本号,我感觉这才是最根本的解决方案,不然只能靠口耳相传“你用的啥版本”、“我咋跑不起来呢”……