兩個真實線上升級故障讓你徹底搞懂package.json中的脫字符

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

"dependencies": {
    "dayjs": "1.11.3",
    "lodash": "~3.9.2",
    "vue": "^2.6.2"
}
復制代碼
dependencies依賴項里的包名作為key,value跟的是版本號。不過心細的同學會注意到:有的版本號前面加了 ~,還有的加了 ^。因為很多同學都是寫業(yè)務,很少關(guān)注這些,平時一般情況下沒問題,但是在一些特殊場景下,如果搞不懂版本號的細節(jié)和原理,會帶來一些很頭疼的經(jīng)歷。

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

一、某中小廠線上真實升級事故
事故背景:
公司后臺管理類項目,用的ui框架為iview。package.json中的版本號為:

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



這個功能在本地和測試環(huán)境都測試過了,沒有問題。我們計劃于2021-06-14日完成上線工作。

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

線上的Modal彈框莫名其妙多了一個灰色蒙層?。∪缦聢D:



我和我的小伙伴都驚呆了,代碼沒動,而且測試環(huán)境都是好的,為什么線上會出問題呢?

事故原因分析:
經(jīng)過對mask的配置排查,我們發(fā)現(xiàn)在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>
復制代碼
首先,我們批評了研發(fā)人員,沒有mask的情況下,不應該把api寫上去,需要保持代碼的整潔。其次,我們開始很痛苦,為什么一份代碼線上和本地測試表現(xiàn)不一致呢?

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

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

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



Modal 的屬性 mask 在 draggable 模式下,不再強制設置為 false。

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

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

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

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

是的,package.json中的版本其實是可控的,具體是怎么控制的呢,接下來就是  ^ 和  ~ 兩個符號正式登場的時候了。

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

4    5    0
重大更新(如重新設計、功能重構(gòu)等)    次要更新(如新增組件、特性升級等)    補丁更新(如小bug修復、細節(jié)優(yōu)化等)
是不是對npm版本號已經(jīng)有一定理解了呢,在此基礎上,我們再來介紹下 ^ 和  ~ 兩個符號的作用:






^和~兩個符號的誕生:
我們先想象下如果版本安裝都是固定的將會是什么樣的場景。npm包也難免會有一些小bug要不定時修復,如果我們的版本號是固定的,那么勢必有一點改動,我們就得改下版本號才能生效。這樣效率是很低的。所以npm的機制規(guī)定:

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

這種好處就是可以更加自由靈活,提高效率。有一些特性更新時,我們再也不需要手動更新版本號了。同樣的,靈活的東西往往也會有弊端,如果我們不注意安裝包的更新,可能就會帶上上述的線上故障。

暫且拋開利弊不說,我們?nèi)绾慰刂平邮芨蠓秶陌姹咎柲兀看鸢妇褪俏覀兩鲜鎏岬降膬蓚€字符。

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

具體使用如下圖:

符號    用法    版本    說明
(^)    ^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的時候,情況會發(fā)生變化。運行npm install并不會重新檢查是否有比您已經(jīng)安裝的更新版本可用。這時候,我們需要使用npm update命令,才能達到我們的更新目的。

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

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

為了加深對package.json中版本號的理解,我們再來看一個更為嚴重,更為官方的故障。

三、react-router官方升級版本號設置沖突導致大規(guī)模應用出錯:
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依賴的特性更新為最新,且已經(jīng)不可用的4.4新特性。

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

最后在來不及修復的情況下,讀者們可以猜一下,最后官方團隊是怎么解決這個問題的。

他們最后在萬般無奈之下,將react-router從4.4直接改成了升級到5.0。。。

最大的版本號更新了之后,脫字符^沒有辦法起到作用,問題也自然沒有了,這就是react-router5誕生的一個小小插曲。

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

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

作者:掘金干貨君

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




作者:掘金干貨君


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