以JS開(kāi)發(fā)為例詳解版本號(hào)的作用與價(jià)值
導(dǎo)讀
在項(xiàng)目開(kāi)發(fā)和運(yùn)行的過(guò)程中,總是少不了各類(lèi)升級(jí)。例如某個(gè)功能組件需要更高的依賴(lài)庫(kù)、數(shù)據(jù)項(xiàng)需要進(jìn)行兼容等等問(wèn)題。遇到此類(lèi)問(wèn)題開(kāi)發(fā)者需要使用版本號(hào)來(lái)解決。今天我們就來(lái)分析一下項(xiàng)目迭代過(guò)程中會(huì)遇到的各類(lèi)升級(jí)問(wèn)題以及如何使用版本號(hào)來(lái)解決。
通常來(lái)說(shuō)升級(jí)會(huì)涉及到三個(gè)點(diǎn):
- 向下兼容
- 協(xié)商升級(jí)
- 拒絕服務(wù)
依賴(lài)升級(jí)
開(kāi)發(fā)者在產(chǎn)品演進(jìn)的過(guò)程中會(huì)不斷的升級(jí)工具依賴(lài),以 npm 版本為例。版本號(hào)通常由三部分組成: 主版本號(hào)、次版本號(hào)和修訂版本號(hào)。
Major.Minor.Patch
其中,Major 表示主版本,當(dāng)你做了不兼容的 API 修改時(shí),就需要升級(jí)這個(gè)版本號(hào)。Minor 表示次版本,當(dāng)你做了向下兼容的功能性新增時(shí),就需要升級(jí)這個(gè)版本號(hào)。而 Patch 表示修訂版本,當(dāng)你做了向下兼容的問(wèn)題修正時(shí),就需要升級(jí)這個(gè)版本號(hào)。
每次在使用 npm install 時(shí)都會(huì)下載
package.json 中的依賴(lài)。而在在依賴(lài)中有 ^ 和 ~ 符號(hào)。其中 ^ 代表次版本兼容,~ 是修訂版本兼容。
{ "devDependencies": { "lib1": "0.15.3", "lib2": "^0.15.3", "lib3": "~0.15.3" } }
如果當(dāng)前三個(gè)庫(kù)都有幾個(gè)高版本,如:
- 0.15.3
- 0.15.4
- 0.16.1
- 1.0.0
在項(xiàng)目下載后執(zhí)行 install ,下載的對(duì)應(yīng)版本則是
- lib1 0.15.3
- lib2 0.16.1
- lib3 0.15.4
雖然 ^ 和 ~ 都不會(huì)升級(jí)破壞性依賴(lài),但版本號(hào)只是“君子協(xié)議”。還是建議大家不要使用這些符號(hào)。同時(shí)之前也遇到過(guò)組件庫(kù)在某個(gè)修訂版本中出現(xiàn)了 bug。雖然很快修復(fù)好了。但是定位問(wèn)題還是需要花費(fèi)一定時(shí)間的。
數(shù)據(jù)緩存
很多情況,開(kāi)發(fā)者為了減少網(wǎng)絡(luò)請(qǐng)求都會(huì)使用數(shù)據(jù)緩存。如果是一個(gè)較為穩(wěn)定的數(shù)據(jù)。我們可以添加版本號(hào)進(jìn)行緩存(同時(shí)添加一個(gè)足夠長(zhǎng)的過(guò)期時(shí)間方便重新獲獲?。?。
以 localStorage 為例,代碼如下所示:
interface Store<T> { /** 存儲(chǔ)數(shù)據(jù) */ data: T; /** * 當(dāng)前版本數(shù)據(jù),可以是一個(gè)數(shù)字或一個(gè)日期字符串 '220101-1' * 后續(xù)的 -1 是為了當(dāng)天發(fā)布多個(gè)版本而準(zhǔn)備的。 */ version: string | number; /** * 過(guò)期時(shí)間 * 可以使用 時(shí)間戳(天數(shù)),天數(shù) dayjs 等 */ expries: string | number; } /** * 實(shí)際存儲(chǔ) key 值 */ const XXX_STORAGE_KEY = 'storageKey'; const isNeedUpgrade = async <T>(): Promise<boolean> => { const storeJSONStr = localStorage.getItem(XXX_STORAGE_KEY); // 沒(méi)有存儲(chǔ) JSON 字符串 if (storeJSONStr === null) { return true; } let store: Partial<Store<T>> = {}; try { store = JSON.parse(storeJSONStr) } catch (e) { // JSON 字符串解析失敗 return true; } const { expries, version: localVersion } = store; // 沒(méi)有過(guò)期時(shí)間獲取當(dāng)前時(shí)間超過(guò)過(guò)期時(shí)間 if (!expries || isOverTime(expries)) { return true; } // 沒(méi)有緩存本地版本 if (!localVersion) { return true; } const currentVersion = await getCurrentVersionForXXXStore(); // 版本不一致 if (currentVersion !== localVersion) { return true; } // 無(wú)需升級(jí) return false; }
當(dāng)前代碼其實(shí)就涉及到了上述所說(shuō)的協(xié)商升級(jí)。
使用版本號(hào)進(jìn)行 api 維護(hù)
隨著業(yè)務(wù)的發(fā)展,數(shù)據(jù)結(jié)構(gòu)不可避免會(huì)發(fā)生一定的改變,如果僅僅只是增加一個(gè)數(shù)據(jù),開(kāi)發(fā)者可以直接在服務(wù)端做一下向下兼容即可。但有些時(shí)候我們可能需要做出一系列的調(diào)整,諸如前一個(gè)版本處理傳遞上來(lái)的 A 和 C 數(shù)據(jù),但是后一個(gè)版本需要處理 A 和 D 數(shù)據(jù)。這時(shí)候我們可能就無(wú)法通過(guò)數(shù)據(jù)傳輸來(lái)確定如何使用。因?yàn)槲覀儫o(wú)法保證服務(wù)端與客戶(hù)端能夠同步升級(jí)。
此時(shí)我們不得不借助版本號(hào)。新版本前端調(diào)用新版本的 API ,舊版本前端調(diào)用舊版本的 API。
/** * 2019-11-12 版本 15 兼容處理了 xxxx, xxxx * 2018-12-10 版本 14 xxxxxx */ const api = initRequest({ // 全局 api 版本號(hào) apiVersion: 15, }); const queryXXX = () => { return api({ // 可以使用 api 版本號(hào),不傳遞默認(rèn)使用全局版本號(hào) apiVersion: 3, }); };
使用或者不使用全局版本號(hào)都有各自的優(yōu)點(diǎn)。使用全局版本號(hào)一個(gè)版本可以同時(shí)進(jìn)行多處修改,方便開(kāi)發(fā)者維護(hù)。但是如果 api 兼容過(guò)多的話(huà),apiVersion 也會(huì)升級(jí)的很快。最終反而不利于維護(hù)。大家可以酌情處理,如果是互聯(lián)網(wǎng)項(xiàng)目,大家可以考慮使用 api 獨(dú)立版本號(hào),如果是企業(yè)服務(wù)則優(yōu)先使用全局版本號(hào)。
大部分情況下服務(wù)端都可以兼容之前的代碼。
@Controller({ path: "user", version: "2", }) export class UserController { @Get() @Version("2") findAll() { return this.userService.findAll(); } @Get() @Version("1") findAllOld() { return this.userService.findAllOld(); } }
極少數(shù)情況下,服務(wù)端代碼難以兼容,或者需要付出極大代價(jià),這時(shí)候就可以拒絕服務(wù)。
@Controller({ path: "user", version: "2", }) export class UserController { @Get() @Version("1") findAllOld() { // 拋出版本不支持異常 throw BusinessException.throwVersionNotSupport(); } }
老的前端代碼中后端拒絕服務(wù)。這樣的話(huà)就不需要在服務(wù)端維護(hù)多個(gè)版本。代碼如下所示:
export const handleVersionError(err) { // 版本不支持和 if (err.errCode !== 'versionNotSupport') { return; } this.$confirm('當(dāng)前版本過(guò)低,無(wú)法正常使用此功能。', '溫馨提示', { confirmButtonText: '刷新頁(yè)面使用最新版本', cancelButtonText: '取消', type: 'warning' }).then(() => { location.reload(); }) }
如果使用小程序開(kāi)發(fā),也可以通過(guò)小程序 API updateManager 重啟升級(jí)。代碼如下所示:
const updateManager = Taro.getUpdateManager(); updateManager.onCheckForUpdate(function (res) { // 請(qǐng)求完新版本信息的回調(diào) console.log(res.hasUpdate); }); updateManager.onUpdateReady(function () { Taro.showModal({ title: "更新提示", content: "新版本已經(jīng)準(zhǔn)備好,是否重啟應(yīng)用?", success: function (res) { if (res.confirm) { // 新的版本已經(jīng)下載好,調(diào)用 applyUpdate 應(yīng)用新版本并重啟 updateManager.applyUpdate(); } }, }); }); updateManager.onUpdateFailed(function () { // 新的版本下載失敗 });
當(dāng)然,針對(duì)小程序開(kāi)發(fā)者還可以存儲(chǔ)當(dāng)前頁(yè)面和獲取信息,如果重啟小程序后,直接打開(kāi)對(duì)應(yīng)界面并刪除信息(添加超時(shí)機(jī)制)。
樂(lè)觀鎖
樂(lè)觀鎖也是利用了版本號(hào)來(lái)實(shí)現(xiàn)的。
當(dāng)一些可變數(shù)據(jù)無(wú)法隔離時(shí)候,開(kāi)發(fā)者可以用兩種不同的控制策略:樂(lè)觀鎖策略和悲觀鎖策略。樂(lè)觀鎖用于沖突檢測(cè),悲觀鎖用于沖突避免。
悲觀者策略非常簡(jiǎn)單,當(dāng) A 用戶(hù)獲取到用戶(hù)信息時(shí)系統(tǒng)把當(dāng)前用戶(hù)信息給鎖定,然后 B 用戶(hù)在獲取用戶(hù)信息時(shí)就會(huì)被告知?jiǎng)e人正在編輯。等到 A 員工進(jìn)行了提交,系統(tǒng)才允許 B 員工獲取數(shù)據(jù)。此時(shí) B 獲取的是 A 更新后的數(shù)據(jù)。
樂(lè)觀者策略則不對(duì)獲取進(jìn)行任何限制,它可以在用戶(hù)信息中添加版本號(hào)來(lái)告知用戶(hù)信息已被修改。樂(lè)觀鎖要求每條數(shù)據(jù)都有一個(gè)版本號(hào),同時(shí)在更新數(shù)據(jù)時(shí)候就會(huì)更新版本號(hào),如 A 員工在更新用戶(hù)信息時(shí)候提交了當(dāng)前的版本號(hào)。系統(tǒng)判斷 A 提交的時(shí)候的版本號(hào)和該條信息版本號(hào)一致,允許更新。然后系統(tǒng)就會(huì)把版本號(hào)修改為新的版本號(hào),B 員工來(lái)進(jìn)行提交時(shí)攜帶的是之前版本號(hào),此時(shí)系統(tǒng)判定失敗。
業(yè)務(wù)也可以根據(jù)情況添加用戶(hù)問(wèn)詢(xún),詢(xún)問(wèn)用戶(hù)是否需要強(qiáng)制更新,在用戶(hù)選擇“是”時(shí)可以添加額外參數(shù)并攜帶之前的版本號(hào)以方便日志信息存儲(chǔ)。
當(dāng)然了,如果當(dāng)前業(yè)務(wù)對(duì)時(shí)間要求沒(méi)有那么高的情況下,開(kāi)發(fā)者也可以直接利用數(shù)據(jù)的更新時(shí)間作為這條數(shù)據(jù)的版本號(hào)。
以上就是以JS開(kāi)發(fā)為例詳解版本號(hào)的作用與價(jià)值的詳細(xì)內(nèi)容,更多關(guān)于JS開(kāi)發(fā)版本號(hào)作用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解JavaScript中數(shù)組的相關(guān)知識(shí)
這篇文章主要介紹了JavaScript中中數(shù)組的相關(guān)知識(shí),是JS入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-07-07JavaScript?Canvas實(shí)現(xiàn)兼容IE的兔子發(fā)射爆破動(dòng)圖特效
這篇文章主要為大家介紹了JavaScript?Canvas實(shí)現(xiàn)兼容IE的兔子發(fā)射爆破動(dòng)圖特效示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01基于JavaScript代碼實(shí)現(xiàn)微信掃一掃下載APP
有很多人在做微信的掃一掃下載。但是在微信更新之后微信將該功能給禁止掉了,也不能說(shuō)是全面禁止吧,因?yàn)轵v訊、微信是一家嘛,通過(guò)應(yīng)用寶審核的應(yīng)用好像還是可以通過(guò)掃一掃直接下載的,下面通過(guò)本篇文章給大家介紹微信掃一掃下載app的代碼片段,感興趣的朋友一起看看吧2015-12-12