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