在我的项目开发和运行的过程中,总是少不了各类降级。例如某个性能组件须要更高的依赖库、数据项须要进行兼容等等问题。遇到此类问题开发者须要应用版本号来解决。明天咱们就来剖析一下我的项目迭代过程中会遇到的各类降级问题以及如何应用版本号来解决。
通常来说降级会波及到三个点:
- 向下兼容
- 协商降级
- 拒绝服务
依赖降级
开发者在产品演进的过程中会一直的降级工具依赖,以 npm 版本为例。版本号通常由三局部组成: 主版本号、次版本号和订正版本号。
Major.Minor.Patch
其中,Major 示意主版本,当你做了不兼容的 API 批改时,就须要降级这个版本号。Minor 示意次版本,当你做了向下兼容的功能性新增时,就须要降级这个版本号。而 Patch 示意订正版本,当你做了向下兼容的问题修改时,就须要降级这个版本号。
每次在应用 npm install 时都会下载
package.json 中的依赖。而在在依赖中有 ^ 和 ~ 符号。其中 ^ 代表次版本兼容,~ 是订正版本兼容。
{
"devDependencies": {
"lib1": "0.15.3",
"lib2": "^0.15.3",
"lib3": "~0.15.3"
}
}
如果以后三个库都有几个高版本,如:
- 0.15.3
- 0.15.4
- 0.16.1
- 1.0.0
在我的项目下载后执行 install , 下载的对应版本则是
- lib1 0.15.3
- lib2 0.16.1
- lib3 0.15.4
尽管 ^ 和 ~ 都不会降级破坏性依赖,但版本号只是“小人协定”。还是倡议大家不要应用这些符号。同时之前也遇到过组件库在某个订正版本中呈现了 bug。尽管很快修复好了。然而定位问题还是须要破费肯定工夫的。
数据缓存
很多状况,开发者为了缩小网络申请都会应用数据缓存。如果是一个较为稳固的数据。咱们能够增加版本号进行缓存(同时增加一个足够长的过期工夫不便从新获获取)。
以 localStorage 为例,代码如下所示:
interface Store<T> {
/** 存储数据 */
data: T;
/**
* 以后版本数据, 能够是一个数字或一个日期字符串 '220101-1'
* 后续的 -1 是为了当天公布多个版本而筹备的。*/
version: string | number;
/**
* 过期工夫
* 能够应用 工夫戳(天数),天数 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;
// 没有过期工夫获取以后工夫超过过期工夫
if (!expries || isOverTime(expries)) {return true;}
// 没有缓存本地版本
if (!localVersion) {return true;}
const currentVersion = await getCurrentVersionForXXXStore();
// 版本不统一
if (currentVersion !== localVersion) {return true;}
// 无需降级
return false;
}
以后代码其实就波及到了上述所说的协商降级。
应用版本号进行 api 保护
随着业务的倒退,数据结构不可避免会产生肯定的扭转,如果仅仅只是减少一个数据,开发者能够间接在服务端做一下向下兼容即可。但有些时候咱们可能须要做出一系列的调整,诸如前一个版本解决传递上来的 A 和 C 数据,然而后一个版本须要解决 A 和 D 数据。这时候咱们可能就无奈通过数据传输来确定如何应用。因为咱们无奈保障服务端与客户端可能同步降级。
此时咱们不得不借助版本号。新版本前端调用新版本的 API,旧版本前端调用旧版本的 API。
/**
* 2019-11-12 版本 15 兼容解决了 xxxx, xxxx
* 2018-12-10 版本 14 xxxxxx
*/
const api = initRequest({
// 全局 api 版本号
apiVersion: 15,
});
const queryXXX = () => {
return api({
// 能够应用 api 版本号,不传递默认应用全局版本号
apiVersion: 3,
});
};
应用或者不应用全局版本号都有各自的长处。应用全局版本号一个版本能够同时进行多处批改,不便开发者保护。然而如果 api 兼容过多的话,apiVersion 也会降级的很快。最终反而不利于保护。大家能够酌情处理,如果是互联网我的项目,大家能够思考应用 api 独立版本号,如果是企业服务则优先应用全局版本号。
大部分状况下服务端都能够兼容之前的代码。
@Controller({
path: "user",
version: "2",
})
export class UserController {@Get()
@Version("2")
findAll() {return this.userService.findAll();
}
@Get()
@Version("1")
findAllOld() {return this.userService.findAllOld();
}
}
极少数状况下,服务端代码难以兼容,或者须要付出极大代价,这时候就能够拒绝服务。
@Controller({
path: "user",
version: "2",
})
export class UserController {@Get()
@Version("1")
findAllOld() {
// 抛出版本不反对异样
throw BusinessException.throwVersionNotSupport();}
}
老的前端代码中后端拒绝服务。这样的话就不须要在服务端保护多个版本。代码如下所示:
export const handleVersionError(err) {
// 版本不反对和
if (err.errCode !== 'versionNotSupport') {return;}
this.$confirm('以后版本过低,无奈失常应用此性能。', '舒适提醒', {
confirmButtonText: '刷新页面应用最新版本',
cancelButtonText: '勾销',
type: 'warning'
}).then(() => {location.reload();
})
}
如果应用小程序开发,也能够通过小程序 API updateManager 重启降级。代码如下所示:
const updateManager = Taro.getUpdateManager();
updateManager.onCheckForUpdate(function (res) {
// 申请完新版本信息的回调
console.log(res.hasUpdate);
});
updateManager.onUpdateReady(function () {
Taro.showModal({
title: "更新提醒",
content: "新版本曾经筹备好,是否重启利用?",
success: function (res) {if (res.confirm) {
// 新的版本曾经下载好,调用 applyUpdate 利用新版本并重启
updateManager.applyUpdate();}
},
});
});
updateManager.onUpdateFailed(function () {// 新的版本下载失败});
当然,针对小程序开发者还能够存储以后页面和获取信息,如果重启小程序后,间接关上对应界面并删除信息(增加超时机制)。
乐观锁
乐观锁也是利用了版本号来实现的。
当一些可变数据无奈隔离时候,开发者能够用两种不同的控制策略:乐观锁策略和乐观锁策略。乐观锁用于冲突检测,乐观锁用于抵触防止。
悲观者策略非常简单,当 A 用户获取到用户信息时零碎把以后用户信息给锁定,而后 B 用户在获取用户信息时就会被告知他人正在编辑。等到 A 员工进行了提交,零碎才容许 B 员工获取数据。此时 B 获取的是 A 更新后的数据。
乐观者策略则不对获取进行任何限度,它能够在用户信息中增加版本号来告知用户信息已被批改。乐观锁要求每条数据都有一个版本号,同时在更新数据时候就会更新版本号,如 A 员工在更新用户信息时候提交了以后的版本号。零碎判断 A 提交的时候的版本号和该条信息版本号统一,容许更新。而后零碎就会把版本号批改为新的版本号,B 员工来进行提交时携带的是之前版本号,此时零碎断定失败。
业务也能够依据状况增加用户问询,询问用户是否须要强制更新,在用户抉择“是”时能够增加额定参数并携带之前的版本号以不便日志信息存储。
当然了,如果以后业务对工夫要求没有那么高的状况下,开发者也能够间接利用数据的更新工夫作为这条数据的版本号。
激励一下
如果你感觉这篇文章不错,心愿能够给与我一些激励,在我的 github 博客下帮忙 star 一下。
博客地址