一、實(shí)際案例
首先,我們來看看 react 最近 6 個月的版本發(fā)布記錄,截圖來自 npmjs.com:

從上圖,我們不難得出幾個結(jié)論:
- 軟件的版本通常由三位組成,形如:X.Y.Z
- 版本是嚴(yán)格遞增的,此處是:15.6.1 -> 15.6.2 -> 16.0.0
- 在發(fā)布重要版本時,可以發(fā)布 alpha、beta、rc 等先行版本
- beta 和 rc 等修飾版本的關(guān)鍵字后面可以帶上次數(shù)和 meta 信息
二、Semver 簡介
在早期,開發(fā)者安裝某個軟件包時,發(fā)現(xiàn)這個軟件包里又依賴不同特定版本的其它軟件包。隨著系統(tǒng)功能越來越復(fù)雜,依賴的軟件包越來越多,依賴關(guān)系也越來越深,這個時候可能面臨版本控制被鎖死的風(fēng)險。這就是「依賴地獄」的問題。
因此,Github 起草了一個具有指導(dǎo)意義的,統(tǒng)一的版本號表示規(guī)則,稱為 Semantic Versioning(語義化版本),即 Semver。目前它是由 npm 的團(tuán)隊(duì)維護(hù)。
Semver 的出現(xiàn),規(guī)定了版本號如何表示,如何增加,如何進(jìn)行比較,不同的版本號意味著什么。遵從了 Semver 規(guī)范的包依賴會非常清晰,不會出現(xiàn)循環(huán)依賴、依賴沖突等常見問題。
關(guān)于 Semver 的完整規(guī)則可以查閱 Semver官網(wǎng)。
下面提煉幾個比較關(guān)鍵的要點(diǎn)。
三、版本格式
版本格式:主版本號.次版本號.修訂號,版本號遞增規(guī)則如下:
- 主版本號(major):當(dāng)你做了不兼容的 API 修改
- 次版本號(minor):當(dāng)你做了向下兼容的功能性新增,可以理解為 Feature 版本
- 修訂號(patch):當(dāng)你做了向下兼容的問題修正,可以理解為 Bug fix 版本

先行版本號及版本編譯信息可以加到「主版本號.次版本號.修訂號」的后面,作為延伸。
四、先行版本
當(dāng)要發(fā)布大版本或者核心的 Feature 時,但是又不能保證這個版本的功能 100% 正常。這個時候就需要通過發(fā)布先行版本。
比較常見的先行版本包括:內(nèi)測版、灰度版本和 RC 版本。Semver 規(guī)范中使用 alpha、beta、rc(以前叫做 gama)來修飾即將要發(fā)布的版本。它們的含義是:
- alpha: 內(nèi)部版本。此版本表示該軟件在此階段主要是以實(shí)現(xiàn)軟件功能為主,通常只在軟件開發(fā)者內(nèi)部交流,一般而言,該版本軟件的 Bug 較多,需要繼續(xù)修改。
- beta: 公測版本。該版本相對于 alpha 版已有了很大的改進(jìn),消除了嚴(yán)重的錯誤,但還是存在著一些缺陷,需要經(jīng)過多次測試來進(jìn)一步消除,此版本主要的修改對像是軟件的 UI,這個階段的版本也會一直加入新的功能。
- rc: 即 Release candiate,正式版本的候選版本。該版本已經(jīng)相當(dāng)成熟了,基本上不存在導(dǎo)致錯誤的 BUG,與即將發(fā)行的正式版相差無幾,不會再加入新的功能了,主要著重于除錯。
比如:1.0.0-alpha.0,1.0.0-alpha.1,1.0.0-beta.0,1.0.0-rc.0,1.0.p-rc.1 等版本。alpha,beta,rc 后需要帶上次數(shù)信息。
最后,當(dāng)經(jīng)過這些先行版本的一系列測試之后,終歸會有一個正式版本,是最終交付用戶使用的一個版本,也就是 Release 版。
五、版本發(fā)布準(zhǔn)則
列舉出比較實(shí)用的一些規(guī)則:
- 標(biāo)準(zhǔn)的版本號必須采用 XYZ 的格式,并且 X、Y 和 Z 為非負(fù)的整數(shù),禁止在數(shù)字前方補(bǔ)零,版本發(fā)布需要嚴(yán)格遞增。例如:1.9.1 -> 1.10.0 -> 1.11.0。
- 某個軟件版本發(fā)行后,任何修改都必須以新版本發(fā)行。
- 1.0.0 的版本號用于界定公共 API。當(dāng)你的軟件發(fā)布到了正式環(huán)境,或者有穩(wěn)定的 API 時,就可以發(fā)布 1.0.0 版本了。
- 版本的優(yōu)先層級指的是不同版本在排序時如何比較。判斷優(yōu)先層級時,必須把版本依序拆分為主版本號、次版本號、修訂號及先行版本號后進(jìn)行比較。
六、npm 包依賴
在 npm 的依賴的規(guī)則中,還有 ~、>、<、=、>=、<=、-、||、x、X、* 等符號。
當(dāng)執(zhí)行 npm install xxx -S 來安裝三方包時,npm 會首先安裝包的最新版本,然后將包名及版本號寫入到 package.json 文件中。被安裝的依賴的版本號前會默認(rèn)加上 ^ 符號。
比如,通過 npm 安裝 vue 時:
{
"dependencies": {
"vue": "^2.5.2"
}
}
-
^:表示同一主版本號中,不小于指定版本號的版本號
`^2.2.1` 對應(yīng)主版本號為 2,不小于 `2.2.1` 的版本號,比如 `2.2.1`、`2.2.2`、`2.3.0`,主版本號固定
// 當(dāng)該依賴有最新版本時(eg:2.3.3),npm install 會安裝最新的依賴
-
~:表示同一主版本號和次版本號中,不小于指定版本號的版本號
`~2.2.1` 對應(yīng)主版本號為 2,次版本號為 2,不小于 `2.2.1` 的版本號,比如 `2.2.1、2.2.2`,主版本號和次版本號固定
-
>、<、=、>=、<=、-:用來指定一個版本號范圍
`>2.1`
`1.0.0 - 1.2.0`
// 注意使用 `-` 的時候,必須兩邊都有空格。
-
||:表示或
`^2 <2.2 ||> 2.3`
-
x、X、*:表示通配符
`*` 對應(yīng)所有版本號
`3.x` 對應(yīng)所有主版本號為 3 的版本號
七、npm 包發(fā)布
通常我們發(fā)布一個包到npm倉庫時,我們的做法是先修改 package.json 為某個版本,然后執(zhí)行 npm publish 命令。手動修改版本號的做法建立在你對 Semver 規(guī)范特別熟悉的基礎(chǔ)之上,否則可能會造成版本混亂。npm 考慮到了這點(diǎn),它提供了相關(guān)的命令來讓我們更好的遵從 Semver 規(guī)范:
- 升級補(bǔ)丁版本號:npm version patch
- 升級小版本號:npm version minor
- 升級大版本號:npm version major
當(dāng)執(zhí)行 npm publish 時,會首先將當(dāng)前版本發(fā)布到 npm registry,然后更新 dist-tags.latest 的值為新版本。
當(dāng)執(zhí)行 npm publish --tag=next 時,會首先將當(dāng)前版本發(fā)布到 npm registry,并且更新 dist-tags.next 的值為新版本。這里的 next 可以是任意有意義的命名(比如:v1.x、v2.x 等等)
OK,接下來用戶就可以通過 npm install package@next 來指定安裝 next 版本的依賴了。
八、npm 中 package-lock.json 的一些坑
在 npm install 后,會生成一個 package-lock.json 文件用于保存當(dāng)前安裝依賴的各種來源及版本號。
在 npm 5.4.2 版本后,package-lock.json 的變動規(guī)則:
- 當(dāng)在 install dependency 的指定版本時,會自動更新 package-lock.json 文件中該 dependency 的 version 到指定的 version
- 當(dāng)在 install dependency 的范圍版本時,當(dāng)前的 version 低于 or 等于 package-lock.json 文件中對應(yīng)的 dependency 的 version 時,會安裝 package-lock.json 中的 version
package.json
"antd": "^3.6.1", // eg:最新版本是 3.9.4
package-lock.json
"antd": "3.7.1",
執(zhí)行npm install 會安裝 3.7.1 版本
如果高于 package-lock.json 中對應(yīng)的 dependency 的 version 時,會安裝當(dāng)前范圍版本號中最高的版本,會更新 package-lock.json 文件中對應(yīng)的版本號。