1. 程式人生 > >你真的知道NPM版本管理規範嗎

你真的知道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

SemVerSemantic 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.71.2.81.2.99等等,但不包括1.2.61.3.0 或1.1.0等等
  • 1.2.7 || >=1.2.9 <2.0.0中包括1.2.71.2.91.4.6等等,但不包括1.2.82.0.0等等

-

連字元表示版本號範圍,表示的是一個閉區間

  • 1.2.3 - 2.3.4 相當於 >=1.2.3和 <=2.3.4

x、X、*

可以替代主版本號.次版本號.修訂號三段中任意一段,表示該位置版本號沒有限制;另外預設三段中任意一段與用xX*替換該段效果相同

  • * 相當於 >=0.0.0,表示任何版本號
  • 1.X1.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