| 名词 | 解释 |
|---|---|
| MR | merge request |
| oci | orange ci 腾讯内部使用的一款CI服务 |
故事是这样的
某天我高高兴兴提了 MR 后,开始干其它活儿坐等 oci 流水线跑完,结果没过一会儿收到企微推送说流水线报错了,从错误日志中立马意识到是 npm 包版本问题引起的。本以为高枕无忧的我顿时菊花一紧,咋肥四?
背景介绍
简单介绍一下这次 MR 我干了什么事情。
由于业务需要,在一个规模比较大的仓库里,新增了两个 devDenpendencies,一个是 commander,另一个是 @tencent/alloyperf(下文简称 alloyperf)。
这里有一个小插曲,由于 alloyperf 近期做了一些优化和升级,于是在开发将近结束的时间点,在某一次合并主干代码操作后,发现程序报错了。
当然报的不是什么毁灭性的错误,而是 ts 类型错误,大致就是旧版本的包有 export 某个 interface,我使用了这个 interface,结果在新版本中居然没有 export 了,导致了报错。
要解决这个问题,肯定是可以解决的,但是我并不想为了这点小事花费时间,也许解决问题只要5分钟,但是解决问题后的善后工作,比如测试,跑流水线,操作过程中程序的等待时间等等,加起来可能就是50分钟。
所以其实有个最简单的方法,就是什么都不要变,锁定最初使用的包版本即可。
解决问题
alloyperf 的现状是这样的:
"@tencent/alloyperf": "^1.6.8"
这里其实已经表明,在装包时是会安装到 1.6.8 往上的版本的。
关于 npm 包版本规范 semantic 不会在本文展开,自行阅读 docs.npmjs.com/about-seman…
简单说就是:
@1.0.0 => must be 1.0.0
^1.0.0 => maybe 1.3.6
~1.0.0 => maybe 1.0.12
如何查看目前安装的版本号?
在 node_modules/@tencent/alloyperf/package.json 的 version 字段可查看。
检查发现果然装的是 1.7.6 版本。
简单,那就装回 1.6.8 版本吧!
npm i @tencent/alloyperf@1.6.8 -D
结果发现 package.json 显示的包版本号依然是
"@tencent/alloyperf": "^1.6.8"
与我期待显示的“1.6.8”不同,于是我再次检查所装包版本,发现正确地装回了 1.6.8 版本,package-lock.json 文件也正确地同步更新。因此可得出结论:
通过命令 npm i @ 精确安装某版本包时,能够正确地安装指定版本包,并更新 package.lock.json,但在 package.json 文件内显示的包版本号,是“^”
包确实是安装回来了,但是 package.json 文件却不能在执行命令结束后,正确显示版本号。那后续的同学执行安装命令时,或是流水线安装包时,不就又装回了最新的 1.7.6 版本了吗?
让我们来做几个测试!
以下每个用例的前置条件:
package.json 显示包版本号为”^1.6.8″
package-lock.json 显示包版本号为”1.6.8″
包实际版本为 “1.6.8”
- 在本地再次执行 npm i
结果:包版本不变。
- 删除 package-lock.json,执行 npm i
结果:生成了 package-lock.json 文件,包版本不变。
- 删除包,执行 npm i
结果:包版本不变。
- 删除 package-lock.json,删除包,执行 npm i
结果:包和 lock 文件的包信息升级到了最新的 1.7.6 版本,package.json 显示的包版本号不变。
可得出以下结论:
执行 npm i 时
如果有 package-lock.json 文件,会按照 lock 文件的包版本号信息进行安装
如果没有 package-lock.json 文件
- 当本地有安装包时,执行后只是会生成 package-lock.json 文件
- 当本地没有安装包时,会安装包的最新版本
npm 文档相关描述

回到问题,我现在希望通过执行单独安装 alloyperf 命令,达到三个效果:
- package.json 显示的包版本号,为命令中指定的安装版本号
- package-lock.json 显示的包版本信息同⬆️要求
- 实际安装包版本同⬆️
我们执行以下两个命令看看:
npm i @tencent/alloyperf -D
包会安装到最新版本,显然不符合我的期望。
npm i @tencent/alloyperf@1.6.8 -D
如上文所提到的那样,除了 package.json 显示包版本号为“^1.6.8”以外,其它均符合期望。
npm 文档相关描述

那么 npm 是否有提供解决上述问题的方法呢?
答案肯定是有的!
在 npm 的文档
docs.npmjs.com/cli/v7/comm… 内有提到:

翻译一下就是,使用这个参数后,在保存依赖时,将以命令中提供的精确版本号,代替 npm 的默认行为。
所以安装命令就应该修改为这样:
npm i @tencent/alloyperf -D -E
我留意到文档 -E 参数描述的末尾有提到 npm's default semver range operator,那么 npm 的这个默认行为是否能够被某种方式更改呢?
可以!
在 npm 的文档 docs.npmjs.com/cli/v7/usin… 内有提到:

也就是说,在每次安装指定包时,并赋予了 -S 或是 -D 参数,npm 的默认行为是会在将包版本号登记进 package.json 时,添加“^”前缀。这个默认前缀是支持更改的。
至此问题解决!
结语
关于 npm 的使用,平常几乎只会在遇到需要新增依赖时,才 npm install 一下,对于 npm install 的过程做了什么事情,很多人可能还是从来没了解过的,建议大家找相关资料好好了解一下,我认为这对于现代前端开发来说是一个必要知识点。
就像 javascript 被很多人吐槽这个那个的坑,但我们仍然每天都在写这个语言,还有后来的 ES6/7/8/9,也有 typescript 等等。就是因为我们深刻了解了 js,才有后来的蓬勃发展。
不说了,我也要补补课了!
花絮
在调试过程中发现 npm 返回的日志中有一行命令
npm fund
印象中经常会在日志中看到,所以去搜了一下,它的作用大致是这样的
列出所有希望别人捐赠 money 的包的列表
俗一点说就是,假如你写了个开源库,希望路人行行好给点钱奖励你的付出,你就可以在 package.json 稍作修改,一旦有项目使用到你的成果,他们就有机会看到你的需求,心情好说不定就给你打钱了!























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)
![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
