「package.json」相信大家都是熟悉的不能在熟悉了。我们经常在「package.json」中发现有这样的依赖项:
"dependencies": {
"dayjs": "1.11.3",
"lodash": "~3.9.2",
"vue": "^2.6.2"
}
dependencies
依赖项里的包名
作为key
,value
跟的是版本号
。不过心细的同学会注意到:有的版本号前面加了 「~「,还有的加了 「^「。因为很多同学都是写业务,很少关注这些,平时一般情况下没问题,但是在一些特殊场景下,如果搞不懂版本号的」细节和原理」,会带来一些很」头疼」的经历。
接下来我们结合两个线上「真实」的故障案例,带大家彻底搞懂「package.json」中的版本号到底是怎么回事。
公司后台管理类项目,用的ui框架为「iview」。「package.json」中的版本号为:
"dependencies": {
"view-design": "^4.5.0"
}
在2021-06-01
日完成一个功能开发,该功能包含一个可以「拖拽」的Modal
弹框,效果和代码如下图:
image.png
这个功能在本地
和测试环境
都测试过了,没有问题。我们计划于2021-06-14
日完成上线工作。
就在2021-06-14
晚上,问题出现了~
线上的Modal
弹框莫名其妙多了一个「灰色蒙层」!!如下图:
image.png
我和我的小伙伴都惊呆了,代码没动,而且测试环境都是好的,为什么线上会出问题呢?
经过对mask
的配置排查,我们发现在Modal
的代码里,写了一个mask=true
的逻辑。
// :mask="true"
<Modal v-model="modal12" draggable scrollable :mask="true" title="Modal 1">
<div>Can be dragged off the screen</div>
</Modal>
首先,我们批评了研发人员,没有mask的情况下,不应该把api写上去,需要「保持代码的整洁」。其次,我们开始很痛苦,为什么一份代码线上
和本地测试
表现不一致呢?
这时候我们想起,线上打包的时候,会把node_modules
删除重新安装,本地和测试环境不会。
果不其然。我们把本地的node_modules
删掉重新npm install
的时候,「复现了这个问题」。
我们开始怀疑是iview的官方包出了问题。于是乎,我们去翻阅iview的文档,在他的更新日志
里发现了这样一段话:
image.png
Modal 的属性 mask 在 draggable 模式下,不再强制设置为 false。
这个更新日志的日期发生在2021-06-11
,正好是我们开发测试到上线的中间日期位置。
相信看到这里,很多同学已经能猜测到原因了,是的,这次事故的原因正是:
本地/测试
环境和线上正式
环境安装了不同版本
(「4.5.0」 vs 「4.6.0」)的iview
。
继续深究,那为什么本地/测试
环境和线上正式
环境会安装出两个版本号呢,难道版本号不是「固定」的吗?
是的,「package.json」中的版本其实是可控的,具体是怎么控制的呢,接下来就是 「^」 和 「~」 两个符号正式登场的时候了。
在学习 「^」 和 「~」 两个符号之前,我们需要先了解一下版本号的概念。所有的npm安装包的版本号都由三位
数字组成,中间以.
间隔。比如iview的版本号4.5.0
。这三位
数字都是有含义的,他们从左到右分别表示:重大更新
.次要更新
.修复补丁
。我们用下图表示:
4 | 5 | 0 |
---|---|---|
重大更新(如重新设计、功能重构等) | 次要更新(如新增组件、特性升级等) | 补丁更新(如小bug修复、细节优化等) |
是不是对npm版本号已经有一定理解了呢,在此基础上,我们再来介绍下 「^」 和 「~」 两个符号的作用:
2 . 「^和~两个符号的诞生:」
我们先想象下如果版本安装都是「固定」的将会是什么样的场景。「npm包」也难免会有一些小bug
要不定时修复,如果我们的版本号是固定的,那么势必有一点改动,我们就得改下版本号才能生效。这样「效率是很低」的。所以npm的机制规定:
npm 允许您接受更大范围的安装包版本,而不是在 package.json 中指定要安装的确切版本。
这种好处就是可以更加「自由灵活,提高效率」。有一些特性更新时,我们再也不需要手动更新版本号了。同样的,灵活的东西往往也会有弊端,如果我们不注意安装包的更新,可能就会带上上述的「线上故障」。
暂且抛开利弊不说,我们如何控制接受更大范围的版本号呢?答案就是我们上述提到的两个字符。
您可以使用波浪号 (~) 允许更新
补丁级别版本(第三位数字)
和脱字符 (^) 更新次要(第二位数字)
或补丁级别(第三位数字)
版本。
具体使用如下图:
符号 | 用法 | 版本 | 说明 |
---|---|---|---|
(^) | ^3.9.2 | 3.. | 1. 向后兼容的新功能 |
2. 废弃特性,但是暂时保留 3. 特性更新/新增 4. bug修复补丁 | | (~) | ~3.9.2 | 3.9.* | 1. bug修复补丁 |
但是有一点需要作为tips
说明:
如果您没有安装过
node_modules
,那么npm install
的时候,情况会按照您的标记符号(^/~)安装指定的最新版本。但是如果您的node_modules
已存在,那么npm install
的时候,情况会发生变化。运行npm install
并不会重新检查是否有比您已经安装的更新版本可用。这时候,我们需要使用npm update
命令,才能达到我们的更新目的。
相信看到这里,同学们应该对package.json中的脱字符(^)有了深入的理解了,上面的线上故障原因也彻底清楚了:
因为线上清除了node_modules
,导致安装的是最新的4.6.0
版本。而此版本中,mask属性已经不是写死的false。一旦写了mask=true,那么背后灰色的背景色就会出现,造成一个线上故障!
为了加深对package.json中版本号的理解,我们再来看一个更为「严重」,更为「官方」的故障。
2019年3月21日。
react-router官方团队,计划将react-router的「4.3.x」版本升级为「4.4.0」版本。他们做了一些特性升级
。然而,react-router的另一个核心衍生库:react-router-dom
,正好依赖了升级前的特性,而react-router-dom
中的package.json恰恰为:
"dependencies": {
"react-router": "^4.3.1"
}
这个脱字符^刚好把react-router-dom
依赖的特性更新为最新,且已经不可用
的4.4
新特性。
可想而知,react-router4.4
版本发布完成之后,react-router-dom
大面积报错。
最后在来不及修复的情况下,读者们可以猜一下,最后官方团队是怎么解决这个问题的。
他们最后在万般无奈之下,将react-router从4.4
直接改成了升级到5.0
。。。
最大的版本号更新了之后,脱字符^没有办法起到作用,问题也自然没有了,这就是react-router5
诞生的一个小小插曲。
(原文链接:react-router诞生记[1])
相信看到这里,对package.json中的脱字符(^),不管是理论层面还是实践层面,各位小伙伴应该都会有比较全面深入的认识了。在关键的时候,说不定可以为你提供很有价值的疑难杂症的线索。在以后的工作中,时不时也要留意下这个版本控制符号哦~
[1]https://reacttraining.com/blog/react-router-v5/#why-the-major-version-bump: https://link.juejin.cn?target=https%3A%2F%2Freacttraining.com%2Fblog%2Freact-router-v5%2F%23why-the-major-version-bump
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8