兩個(gè)真實(shí)線上升級(jí)故障讓你徹底搞懂package.json中的脫字符

package.json相信大家都是熟悉的不能在熟悉了。我們經(jīng)常在package.json中發(fā)現(xiàn)有這樣的依賴項(xiàng):

"dependencies": {
    "dayjs": "1.11.3",
    "lodash": "~3.9.2",
    "vue": "^2.6.2"
}
復(fù)制代碼
dependencies依賴項(xiàng)里的包名作為key,value跟的是版本號(hào)。不過(guò)心細(xì)的同學(xué)會(huì)注意到:有的版本號(hào)前面加了 ~,還有的加了 ^。因?yàn)楹芏嗤瑢W(xué)都是寫(xiě)業(yè)務(wù),很少關(guān)注這些,平時(shí)一般情況下沒(méi)問(wèn)題,但是在一些特殊場(chǎng)景下,如果搞不懂版本號(hào)的細(xì)節(jié)和原理,會(huì)帶來(lái)一些很頭疼的經(jīng)歷。

接下來(lái)我們結(jié)合兩個(gè)線上真實(shí)的故障案例,帶大家徹底搞懂package.json中的版本號(hào)到底是怎么回事。

一、某中小廠線上真實(shí)升級(jí)事故
事故背景:
公司后臺(tái)管理類項(xiàng)目,用的ui框架為iview。package.json中的版本號(hào)為:

"dependencies": {
   "view-design": "^4.5.0"
}
復(fù)制代碼
在2021-06-01日完成一個(gè)功能開(kāi)發(fā),該功能包含一個(gè)可以拖拽的Modal彈框,效果和代碼如下圖:



這個(gè)功能在本地和測(cè)試環(huán)境都測(cè)試過(guò)了,沒(méi)有問(wèn)題。我們計(jì)劃于2021-06-14日完成上線工作。

就在2021-06-14晚上,問(wèn)題出現(xiàn)了~

線上的Modal彈框莫名其妙多了一個(gè)灰色蒙層!!如下圖:



我和我的小伙伴都驚呆了,代碼沒(méi)動(dòng),而且測(cè)試環(huán)境都是好的,為什么線上會(huì)出問(wèn)題呢?

事故原因分析:
經(jīng)過(guò)對(duì)mask的配置排查,我們發(fā)現(xiàn)在Modal的代碼里,寫(xiě)了一個(gè)mask=true的邏輯。

//  :mask="true"
<Modal v-model="modal12" draggable scrollable :mask="true" title="Modal 1">
    <div>Can be dragged off the screen</div>
</Modal>
復(fù)制代碼
首先,我們批評(píng)了研發(fā)人員,沒(méi)有mask的情況下,不應(yīng)該把a(bǔ)pi寫(xiě)上去,需要保持代碼的整潔。其次,我們開(kāi)始很痛苦,為什么一份代碼線上和本地測(cè)試表現(xiàn)不一致呢?

這時(shí)候我們想起,線上打包的時(shí)候,會(huì)把node_modules刪除重新安裝,本地和測(cè)試環(huán)境不會(huì)。

果不其然。我們把本地的node_modules刪掉重新npm install的時(shí)候,復(fù)現(xiàn)了這個(gè)問(wèn)題。

我們開(kāi)始懷疑是iview的官方包出了問(wèn)題。于是乎,我們?nèi)シ唅view的文檔,在他的更新日志里發(fā)現(xiàn)了這樣一段話:



Modal 的屬性 mask 在 draggable 模式下,不再?gòu)?qiáng)制設(shè)置為 false。

這個(gè)更新日志的日期發(fā)生在2021-06-11,正好是我們開(kāi)發(fā)測(cè)試到上線的中間日期位置。

相信看到這里,很多同學(xué)已經(jīng)能猜測(cè)到原因了,是的,這次事故的原因正是:

本地/測(cè)試環(huán)境和線上正式環(huán)境安裝了不同版本(4.5.0 vs 4.6.0)的iview。

繼續(xù)深究,那為什么本地/測(cè)試環(huán)境和線上正式環(huán)境會(huì)安裝出兩個(gè)版本號(hào)呢,難道版本號(hào)不是固定的嗎?

是的,package.json中的版本其實(shí)是可控的,具體是怎么控制的呢,接下來(lái)就是  ^ 和  ~ 兩個(gè)符號(hào)正式登場(chǎng)的時(shí)候了。

二、package.json中版本控制詳解
npm包的版本號(hào)構(gòu)成:
在學(xué)習(xí) ^ 和  ~ 兩個(gè)符號(hào)之前,我們需要先了解一下版本號(hào)的概念。所有的npm安裝包的版本號(hào)都由三位數(shù)字組成,中間以.間隔。比如iview的版本號(hào)4.5.0。這三位數(shù)字都是有含義的,他們從左到右分別表示:重大更新.次要更新.修復(fù)補(bǔ)丁。我們用下圖表示:

4    5    0
重大更新(如重新設(shè)計(jì)、功能重構(gòu)等)    次要更新(如新增組件、特性升級(jí)等)    補(bǔ)丁更新(如小bug修復(fù)、細(xì)節(jié)優(yōu)化等)
是不是對(duì)npm版本號(hào)已經(jīng)有一定理解了呢,在此基礎(chǔ)上,我們?cè)賮?lái)介紹下 ^ 和  ~ 兩個(gè)符號(hào)的作用:






^和~兩個(gè)符號(hào)的誕生:
我們先想象下如果版本安裝都是固定的將會(huì)是什么樣的場(chǎng)景。npm包也難免會(huì)有一些小bug要不定時(shí)修復(fù),如果我們的版本號(hào)是固定的,那么勢(shì)必有一點(diǎn)改動(dòng),我們就得改下版本號(hào)才能生效。這樣效率是很低的。所以npm的機(jī)制規(guī)定:

npm 允許您接受更大范圍的安裝包版本,而不是在 package.json 中指定要安裝的確切版本。

這種好處就是可以更加自由靈活,提高效率。有一些特性更新時(shí),我們?cè)僖膊恍枰謩?dòng)更新版本號(hào)了。同樣的,靈活的東西往往也會(huì)有弊端,如果我們不注意安裝包的更新,可能就會(huì)帶上上述的線上故障。

暫且拋開(kāi)利弊不說(shuō),我們?nèi)绾慰刂平邮芨蠓秶陌姹咎?hào)呢?答案就是我們上述提到的兩個(gè)字符。

您可以使用波浪號(hào) (~) 允許更新補(bǔ)丁級(jí)別版本(第三位數(shù)字)和脫字符 (^) 更新次要(第二位數(shù)字)或補(bǔ)丁級(jí)別(第三位數(shù)字)版本。

具體使用如下圖:

符號(hào)    用法    版本    說(shuō)明
(^)    ^3.9.2    3..    1. 向后兼容的新功能 2. 廢棄特性,但是暫時(shí)保留 3. 特性更新/新增 4. bug修復(fù)補(bǔ)丁
(~)    ~3.9.2    3.9.*    1. bug修復(fù)補(bǔ)丁
但是有一點(diǎn)需要作為tips說(shuō)明:

如果您沒(méi)有安裝過(guò)node_modules,那么npm install的時(shí)候,情況會(huì)按照您的標(biāo)記符號(hào)(^/~)安裝指定的最新版本。但是如果您的node_modules已存在,那么npm install的時(shí)候,情況會(huì)發(fā)生變化。運(yùn)行npm install并不會(huì)重新檢查是否有比您已經(jīng)安裝的更新版本可用。這時(shí)候,我們需要使用npm update命令,才能達(dá)到我們的更新目的。

相信看到這里,同學(xué)們應(yīng)該對(duì)package.json中的脫字符(^)有了深入的理解了,上面的線上故障原因也徹底清楚了:

因?yàn)榫€上清除了node_modules,導(dǎo)致安裝的是最新的4.6.0版本。而此版本中,mask屬性已經(jīng)不是寫(xiě)死的false。一旦寫(xiě)了mask=true,那么背后灰色的背景色就會(huì)出現(xiàn),造成一個(gè)線上故障!

為了加深對(duì)package.json中版本號(hào)的理解,我們?cè)賮?lái)看一個(gè)更為嚴(yán)重,更為官方的故障。

三、react-router官方升級(jí)版本號(hào)設(shè)置沖突導(dǎo)致大規(guī)模應(yīng)用出錯(cuò):
2019年3月21日。

react-router官方團(tuán)隊(duì),計(jì)劃將react-router的4.3.x版本升級(jí)為4.4.0版本。他們做了一些特性升級(jí)。然而,react-router的另一個(gè)核心衍生庫(kù):react-router-dom,正好依賴了升級(jí)前的特性,而react-router-dom中的package.json恰恰為:

"dependencies": {
   "react-router": "^4.3.1"
}
復(fù)制代碼
這個(gè)脫字符^剛好把react-router-dom依賴的特性更新為最新,且已經(jīng)不可用的4.4新特性。

可想而知,react-router4.4版本發(fā)布完成之后,react-router-dom大面積報(bào)錯(cuò)。

最后在來(lái)不及修復(fù)的情況下,讀者們可以猜一下,最后官方團(tuán)隊(duì)是怎么解決這個(gè)問(wèn)題的。

他們最后在萬(wàn)般無(wú)奈之下,將react-router從4.4直接改成了升級(jí)到5.0。。。

最大的版本號(hào)更新了之后,脫字符^沒(méi)有辦法起到作用,問(wèn)題也自然沒(méi)有了,這就是react-router5誕生的一個(gè)小小插曲。

(原文鏈接:react-router誕生記)

四、總結(jié)
相信看到這里,對(duì)package.json中的脫字符(^),不管是理論層面還是實(shí)踐層面,各位小伙伴應(yīng)該都會(huì)有比較全面深入的認(rèn)識(shí)了。在關(guān)鍵的時(shí)候,說(shuō)不定可以為你提供很有價(jià)值的疑難雜癥的線索。在以后的工作中,時(shí)不時(shí)也要留意下這個(gè)版本控制符號(hào)哦~

作者:掘金干貨君

鏈接:https://juejin.cn/post/7121520457760653349




作者:掘金干貨君


歡迎關(guān)注微信公眾號(hào) :深圳灣碼農(nóng)