semver:語義化版本規範在 Node.js 中的實現
semver
n我們先從 semver 這個不起眼的包開始,它是 語義化版本(Semantic Versioning)規範 的一個實現,目前是由 npm 的團隊維護的,實現了版本和版本範圍的解析、計算、比較,在 NPM 的被依賴(Most depended-upon)榜單中排名 34.
nSemantic Versioning 是由 GitHub 的聯合創始人 Tom Preston-Werner 建立的一個有關如何命名軟體和庫(包)版本的規範,用以解決在大型項目中對依賴的版本失去控制的問題(例如你可能因為害怕不兼容而不敢去更新依賴)。現在 Semantic Versioning 已經在開源社區中得到了廣泛的認同,Node.js 的包管理工具 npm 也完全基於 Semantic Versioning 來管理依賴的版本。semver 定義了兩種概念:
- 版本是指例如 0.4.1、1.2.7、1.2.4-beta.0 這樣表示包的特定版本的字元串。
- 範圍則是對滿足特定規則的版本的一種表示,例如 1.2.3-2.3.4、1.x、^0.2、>1.4.
顯然這個包的使用場景就是與版本號打交道。例如你有一個客戶端工具,需要在每次啟動時向伺服器發起一個查詢來檢查更新,那麼用 semver 去比較版本將會是一個很好的選擇:
console.log(semver.gt(lastestVersion, currentVersion) ? New Update available : Already lastest version);n
基於 Semantic Versioning 規範,我們還可以計算出兩個版本之間的差異程度:
console.log(semver.diff(lastestVersion, currentVersion) == major ? Major Release : Minor or patch Release );n
n再比如你在實現一個插件化的系統,每個插件(在 package.json 中)都會聲明一個所兼容的主程序的版本範圍,而主程序在載入插件時需要檢查這個版本並使當地給出警告:
plugins.forEach(function(plugin) {n if (!semver.satisfies(platformVersion, plugin.engines.platform))n console.log(plugin.name, require, plugin.engines.platform, but unable to meet);n});n
n在你使用 express 設計一個支持多種版本的 API 伺服器時,你可以這樣做:
app.get(/, apiVersion(<0.6), function(req, res) {n res.send(Less than 0.6);n});nnapp.get(/, apiVersion(1.2.3 - 2.3.4), function(req, res) {n // ...n});nnapp.get(/, apiVersion(*), function(req, res) {n res.send(Unsupported version);n});n
n其中 apiVersion 中間件的一個簡單實現:
function apiVersion(range) {n return function(req, res, next) {n if (semver.satisfies(req.headers[x-api-version], range))n next();n elsen next(route); // skip current routen };n}n
也許你用字元串計算再配合一點正則也可以完成上述的場景,但你很難做到對 Semantic Versioning 的完備支持,在之後發布新版本後撰寫版本號的時候也會受到限制,例如 semver 可以正確地比較 0.9.0 和 0.10.0 以及 0.9.0-beta.1,但自行實現這些支持將會非常繁瑣。
所以其實選擇 semver 的理由很簡單 —— 讓專業的包去完成專業的工作,相信這也是在 Node.js 社區得到了廣泛認同的觀點。
推薦閱讀: