兩個真實線上升級故障讓你徹底搞懂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)