乐趣区

关于npm:Npm依赖版本变动引发的惨案

来了新共事,拉同一个我的项目到本地装置依赖之后跑不起来,然而其余三台电脑运行着都没问题。接下来就是逐渐定位问题,首先排除了代码问题,因为最新代码在其余共事不同零碎的电脑上都没失常运行,进过百度 / 谷歌 /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.jsonnpm-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

依赖版本锁定计划

大略有这么几条计划:

  1. package.json中固定版本
  2. npm+package-lock.json
  3. npm+npm-shrinkwrap.json
  4. 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.jsonpackage-lock.josn 原理相似,习惯用 yarn 命令的能够采取这种形式。

注:

如果我的项目中同时存在 package-lock.jsonnpm-shrinkwrap.json,npm5 只会更新它,而不会生成 package-lock.json。

yarn 的锁定版本文件叫 yarn.lock,目前公布平台是反对的,不过最好保障我的项目中只有一个版本锁定文件,package-lock.json、npm-shrinkwrap.json 或者 yarn.lock 二选一,防止出现装置后果和料想不统一的状况

npmcnpm 的区别

  • cnpm i不受 package-lock.json 影响,只会依据 package.json 进行下载。
  • cnpm i xxx@xxx不会更新到 package-lock.json 中去。
  • npm i xxx@xxx会跟新到 package-lock.json 中去

限时秒杀阿里云服务器 ECS、云数据库 MySQL、对象存储 OSS 等多种代金券

参考

npm shrinkwrap

那些年应用 npm 进行依赖管理所踩的坑

npm-shrinkwrap 锁定依赖

npm5.0 新增 package-lock.json 文件挖的坑

前端外围工具:yarn、npm、cnpm 三者如何优雅的在一起应用?

npm 的 package.json 和 package-lock.json 更新策略

Nrm 装置与配置

应用 npm shrinkwrap 来治理我的项目依赖

npm 的更新策略

npm ci 命令

退出移动版