你真的知道NPM版本管理規範嗎
NPM Version Management Specification
來源
常規的開發,常規的程式碼,不動如山的CI,突然發生了錯誤,導致失敗,出現以下錯誤:
1
|
Build failed: [BABEL] /xxx/xxx/yyy/.xxx.js: You gave us a visitor for the node type OptionalCallExpression but it's not a valid type
|
本地除錯之,赫然出現了不一樣的錯誤:
1
|
Build failed: Cannot find module '@babel/runtime/core-js/object/keys' |
觀察了一下package.json
,含有"babel-runtime": "^6.9.2"
,於是乎開開心心的安裝了下@babel/runtime
=> npm install @babel/runtime
。BOOOOOM!繼續報錯,尋遍 issue 未發現錯誤原因以及真正的解決辦法,TnT
查看了下框架包,查找了下專案依賴包的依賴包,發現使用了@babel/[email protected]
的版本,莫不是版本問題?!換之,修改了下package.json
檔案如下:
12
|
- "babel-runtime": "^6.9.2"+ "@babel/runtime": "^7.0.0-beta.41"
|
常規rm -rf node_modules && cnpm install
,小段時間的等待之後,發現錯誤並沒有消失,奇了怪了~~
繼續檢視依賴包的依賴包,發現它要7.0.0-beta.41
,而在我的node_modules/
黑洞裡的@babel/runtime
卻安裝的是7.0.0
版本,Bingo,問題找到了,鎖個版本,修改如下:
12
|
- "@babel/runtime": "^7.0.0-beta.41"+ "@babel/runtime": "7.0.0-beta.41"
|
常規rm -rf node_modules && cnpm install
之後,問題消失了,部署跑CI瞧一下,問題解決。
簡單的一個問題,在知道原因之後。如果不知道原因呢??(此處有個黑人問號)
幸好我知道些npm
版本的控制規範,才得已比較早的定位問題並解決之,帶著這份小確幸,重新整理了下npm
包管理器的版本管理規範(NPM Version Management Specification)。
語義化版本控制規範 SemVer
SemVer
(Semantic Versioning,語義化版本控制)是Github起草的一個語義化版本號管理模組,它實現了版本號的解析和比較,規範版本號的格式,它解決了依賴地獄的問題。
基本規則
語義化版本控制,顧名思義,就是讓版本號更具有語義,可以傳達出關於軟體本身的一些重要資訊而不只是簡單的一串數字。
基本版本格式
1
|
主版本號(Major).次版本號(Minor).修訂號(Patch)
|
每個部分都為整數(>=0
),按照遞增的規則改變。
版本號遞增規則
- 主版本號(Major):當你做了不相容的API修改
- 次版本號(Minor):當你做了向下相容的功能性新增
- 修訂號(Patch):當你做了向下相容的問題修正
- 先行版本號及版本編譯資訊可以加到
基本版本格式
的後面,作為延伸- 先行版本號由首位的連線號”-“、識別符號號(由ASCII碼的英文數字和連線號識別符號[0-9A-Za-z-]組成)、句點”.“組成。如1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。先行版的優先順序低於相關聯的標準版本
- 版本編譯資訊由首位的一個加號和一連串以句點分隔的識別符號號(由ASCII碼的英文數字和連線號識別符號[0-9A-Za-z-]組成)組成。如1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。判斷版本優先層級時,版本編譯資訊可以被忽略
如何比較版本高低
判斷優先層級時,必須把版本依序拆分為主版本號、次版本號、修訂號及先行版本號後進行比較。由左到右依次比較每個識別符號號,第一個差異值用來決定優先層級(其中字母連線號以ASCII排序進行比較、其他都相同時欄位多的先行版本號優先順序較高)。如:
1
|
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。
|
範圍規則
<空>
鎖定版本號
1.0.0
: 鎖定了版本只能為1.0.0
<、<=、>、>=、=
指定版本範圍,甚至可以通過||
組合多個比較器
=1.2.7 <1.3.0
中包括1.2.7
、1.2.8
、1.2.99
等等,但不包括1.2.6
、1.3.0
或1.1.0
等等1.2.7 || >=1.2.9 <2.0.0
中包括1.2.7
、1.2.9
、1.4.6
等等,但不包括1.2.8
或2.0.0
等等
-
連字元表示版本號範圍,表示的是一個閉區間
1.2.3 - 2.3.4
相當於>=1.2.3
和<=2.3.4
x、X、*
可以替代主版本號.次版本號.修訂號
三段中任意一段,表示該位置版本號沒有限制;另外預設三段中任意一段與用x
、X
或*
替換該段效果相同
*
相當於>=0.0.0
,表示任何版本號1.X
或1.x
相當於>=1.0.0 <2.0.0
,匹配到主版本號1.2.*
相當於>=1.2.0 <1.3.0
,匹配到主版本號和次版本號""
(空字串) 相當於*
,即相當於>=0.0.0
1
相當於1.x.x
,即相當於>=1.0.0 <2.0.0
1.2
相當於1.2.x
,即相當於>=1.2.0 <1.3.0
~
允許小版本迭代
- 如果有預設值,預設部分任意迭代;
- 如果沒有預設值,只允許補丁即修訂號(Patch)的迭代
eg.:
~1.2.3
:>=1.2.3 <1.3.0
~1.2
:>=1.2.0 < 1.3.0
(相當於1.2.x
)~1
:>=1.0.0 <2.0.0
(相當於1.x
)~0.2.3
:>=0.2.3 <0.3.0
~0.2
:>=0.2.0 <0.3.0
(相當於0.2.x
)~0
:>=0.0.0 <1.0.0
(相當於0.x
)~1.2.3-beta.2
:>=1.2.3-beta.2 <1.3.0
(注意,在1.2.3
版本中,允許使用大於等於beta.2
的先行版本號,而除1.2.3
之外的版本號不允許使用先行版本號,所以此處1.2.3-beta.4
是允許的,而1.2.4-beta.2
是不允許的)
^
允許大版本迭代
- 允許從左到右的第一段不為
0
那一版本位+1
迭代(左閉右開); - 如果有預設值,且預設值之前沒有不為0的版本位,則允許預設值的前一位版本
+1
迭代
eg.:
^1.2.3
:>=1.2.3 <2.0.0
^0.2.3
:>=0.2.3 <0.3.0
^0.0.3
:>=0.0.3 <0.0.4
^1.2.x
:>=1.2.0 <2.0.0
^0.0.x
:>=0.0.0 <0.1.0
^0.0
:>=0.0.0 <0.1.0
^1.x
:>=1.0.0 <2.0.0
^0.x
:>=0.0.0 <1.0.0
^1.2.3-beta.2
:>=1.2.3-beta.2 <2.0.0
(注意,在1.2.3
版本中,允許使用大於等於beta.2
的先行版本號,而除了1.2.3
之外的版本號不允許使用先行版本號,所以此處1.2.3-beta.4
是允許的,而1.2.4-beta.2
是不允許的);^0.0.3-beta
:>=0.0.3-beta <0.0.4
(同上,此處0.0.3-pr.2
是允許的)
鎖定(控制)版本
看到這,聰明的你一定想到了package-lock.json
或是yarn.lock
。
在npm
的版本>=5.1
的時候,package-lock.json
檔案是自動開啟的,意味著會自動生成,package-lock.json
(官方文件)可以理解為/node_modules
資料夾內容的json
對映,並能夠感知npm
的安裝/升級/解除安裝的操作。可以保證在不同的環境下安裝的包版本保持一致。聽上去很不錯哈,實際使用中,大部分它的表現確實不錯,可是如上述問題:我手動修改了package.json
檔案內依賴的版本,package-lock.json
就沒那麼聰明(至少目前是,未來會不會變聰明就不可知了),且不會變化。於是BOOOOOOM~~~~
SO
如果你真的想保證你的包版本在各個環境都是一樣的話,請修改下package.json
中的依賴,去掉預設前面的^
,當然這樣的話,你就沒法自動享受依賴包小版本的修復了,問題來了,在什麼情況下選擇哪一種呢?
- 在依賴包嚴格按照版本規範來開發的,你可以使用
^
來享受包的最新功能和修復。這也是推薦的。 - 在你不可知或已知依賴包不是那麼規範的情況下,或許它在一個小版本(
patch
)做出不相容更改(不相容更改在beta
等先行版本中一定[墨菲定律]會發生),那麼這個時候,你應該把這個依賴包的版本在package.json
上鎖住版本,而不應該把它交給package-lock.json
來處理 - 記住一點,絕對不要在生成環境下使用
beta
等先行版本依賴包,因為如果那是你的私有專案,它會在未來的某一刻坑害了你,如果這是你的共有專案,那麼,它一定會在未來的某一刻對你的所有使用者做出致命的坑害行為!(beta
包就是不負責任的流氓包,玩覺爽就好 ^o^)
最後:rm -rf node_modules/ && npm install
大法在你使用package-lock
的情況下,請更換為:rm -rf node_modules && rm -rf package-lock.json && npm install
。