http-api-model
v1.0.12
Published
A api Model 。一个接口模型。
Downloads
8
Maintainers
Readme
安装
npm install http-api-model
简要说明
对于axios的Model化封装,参考php laravel orm,希望在前端也能有类似后端的模型概念。
使用localForage 提供模型本地缓存功能。针对每次请求的参数来存储缓存数据。
使用时包后,继承 Model 开始使用。
更新
缓存配置 single 为true 时,空参数将尝试获取缓存数据。
简要示例,包含基础模型,登录模型,数据模型。通常一个class 一个文件
import {Model} from "http-api-model";
import type {AxiosResponse} from "axios";
// 假定下面是 MyBaseModel.ts 文件
// 项目基础模型
export default class MyBaseModel extends Model {
constructor() {
super();
this.config({
baseUrl: '/apiRoot',
method: 'post'
});
this.cache.config({
use: true,
// 使用分账号缓存
account: () => MyBaseModel.cacheAccount,
// account:()=>MyBaseModel.loginModel?.data.account,
})
}
// 静态 token 和 cacheAccount 为自行扩展实例,模型本身没有这部分。
protected static token = '';
// 缓存账号值,通过登录接口设置
protected static cacheAccount = ''
// 可以考虑挂载 登录接口。注意只放类型,避免 import 错误。
// protected static loginModel?:Login
// 请求前处理config ,可添加登录信息
protected handleRequest() {
this._config.headers = this._config.headers || {};
this._config.headers['token'] = MyBaseModel.token
}
// 自行根据接口格式,判断请求时成功还是失败
protected handleIsSuccess(r: AxiosResponse): boolean {
return r.status === 200;
}
// 自行根据请求格式,获取模型数据
protected handleResponseData(r: AxiosResponse): this['data'] {
// 获取接口中的数据
return r.data.returnContent;
}
// handleIsSuccess false 时
protected handleFail(r: AxiosResponse) {
// 请求失败了,如何处理,如展示异常提示
}
protected handleError(e: any) {
//请求异常处理
}
protected handleEnd() {
// 相当于 Promise.finally
// 接口结束时调用,无论失败还是异常,都会执行。
}
}
// 假定下面是 Login.ts 文件
export default class Login extends MyBaseModel {
constructor() {
super();
this.config({
url: 'xxx/xx'
})
}
// 请求参数
query = {
account: '',
password: ''
}
// 模型数据
data = {
token: '',
account: '',
name: '',
age: undefined as number | undefined,
heigiht: 0,
}
protected handleResponseData(r: AxiosResponse): this['data'] {
// 获取接口中的数据
const mData = r.data.returnContent as this['data'];
// 设置项目身份凭证
MyBaseModel.token = mData.token;
MyBaseModel.cacheAccount = mData.account;
return mData;
}
}
const login = new Login();
login.fetch()
// 假定下方是 MyUser.ts 文件
export default class MyUser extends MyBaseModel {
constructor() {
super();
this.config({
// 这个接口用get
method: 'get',
url: 'xxx/xx'
})
}
// 请求参数
query = {
id: ''
}
// 模型数据
data = {
name: '',
age: undefined as number | undefined,
heigiht: 0,
}
save() {
return new MyUserSave().setQuery(this.data).fetch()
}
}
// 用户保存接口 和 用户详情强相关,且仅用与保存。
// 这样的接口,可以放到 MyUser 模型文件中
class MyUserSave extends MyBaseModel {
constructor() {
super();
this.config({
// 这个接口用get
method: 'post',
url: 'xxx/xx'
})
}
// 请求参数
query: MyUser['data'] = {
// 这里不重复写了
}
}
// 这里假定已登录
const user = new MyUser();
// 设置查询参数
// user.query.id = 'id1';
user.setQuery({id: 'id1'})
// 发起请求或获取缓存
user.fetch().then(v => {
// 使用数据
// console.log(v.data.name);
console.log(user.data.name);
})
Model 详细内容说明
import type { AxiosRequestConfig, AxiosResponse } from "axios";
import ModelCache from "./ModelCache";
export default class Model {
/**
* 模型初始化。将在调用设置config时执行,若不需要设置config,请手动执行它
* @protected
*/
protected initModel(): void;
/**
* 初始的请求参数
* @private
*/
private initQuery;
/**
* 上次请求参数
* @private
*/
private lastQuery;
/**
* 初始数据
* @private
*/
private init;
/**
* 上次请求数据
* @private
*/
private _last;
/**
* 上次请求结果数据
*/
get last(): this['data'];
private _isSuccess;
protected _config: AxiosRequestConfig;
/**
* 设置请求配置并初始化模型,应只在construct调用
* 也应在每个 模型 construct 中调用一次 this.config({});以完成初始化
* @param options
*/
config(options: AxiosRequestConfig): this;
/**
* 获取请求配置,不太常用
*/
config(): AxiosRequestConfig;
/**
* 定义请求参数
*/
query: any;
/**
* 批量设置请求参数
* @param q
*/
setQuery(q: Partial<this['query']>): this;
/**
* 将参数重置回上次请求时
*/
resetQuery(): void;
/**
* 重置回最初参数
* 请在 construct 中 initModel 或 设置 config
* 否则可能不准确
*/
clearQuery(): void;
/**
* 模型数据,从接口获取的数据(或从缓存),通常为数据字典。
* 注意,data应为普通数据类型,不能含有函数,getter setter。否则可能缓存不全或异常。
*/
data: any;
/**
* 批量设置模型数据
* @param r
*/
setData(r: Partial<this['data']>): this;
/**
* 将模型数据重置回上次请求的数据
*/
reset(): void;
/**
* 将数据重置回初始化
* 请在 construct 中 initModel 或 设置 config
* 否则可能不准确
*/
clearData(): void;
private updateLast;
/**
* 请求是否成功,仅为 true 表示请求成功 false也能是还未请求
*/
get isSuccess(): boolean;
/**
* 缓存控制类
* @protected
*/
readonly cache: ModelCache<this>;
private _isCache;
/**
* 是否是缓存数据
*/
get isCache(): boolean;
protected _isFail: boolean;
/**
* 模型获取数据,从缓存或api获取数据
*/
fetch(): Promise<this>;
/**
* 缓存模型数据
* 只会经历 handleError handleEnd
*/
saveCache(): Promise<this>;
/**
* 接口发送时需要的参数,可抛出异常,终止请求
* 返回值说明
* default 存在时,会忽略 params data
* default中的值,会根据请求类型 放入 get 或 post参数
* params 会放入 AxiosRequestConfig.params
* data 对应 AxiosRequestConfig.data
* @protected
*/
protected handleTransformQuery(): {
default?: any;
params?: any;
data?: any;
};
/**
* 在 handleTransformQuery 后执行
* 请求配置处理,可修改config,比如设置登录token
* 也可在此将上传数据,转为 FormData
* @protected
*/
protected handleRequest(): void;
/**
* 请求成功时执行,
* 数据是否正确,默认 httpCode 200状态成功 AxiosResponse.status === 200;
* 假设 接口格式 {status:1,msg:'',content} AxiosResponse.data.status === 1;
* @param r
* @protected
*/
protected handleIsSuccess(r: AxiosResponse): boolean;
/**
* 数据正确时执行(handleIsSuccess true 执行)
* 根据自己的接口规则,从AxiosResponse中返回模型数据。
* 注意返回原始值,当心返回 Proxy 等对象或含有函数,可能会导致异常。
* 默认返回 AxiosResponse.data
* 举例 接口格式 {code:1,returnContent:{},msg:''},应返回 AxiosResponse.data.returnContent
* 也可自行调整接口数据格式,如 接口 {userName:'',userAage:0}-> 模型data{name:'',age:0}
* @param r
* @protected
*/
protected handleResponseData(r: AxiosResponse): this['data'];
/**
* 数据异常时执行(handleIsSuccess false 执行)
* 处理接口数据状态异常
* @param r
* @protected
*/
protected handleFail(r: AxiosResponse): void;
/**
* 请求错误处理
* @param e
* @protected
*/
protected handleError(e: any): void;
/**
* 请求结束处理 等于 Promise.finally,无论成功失败都会执行
* 可以进行 接口异常日志发送等操作
* @protected
*/
protected handleEnd(): void;
private _loading;
/**
* 模型是否请求中
*/
get loading(): boolean;
private set loading(value);
/**
* 模型loading变化处理,如可控制展示 全屏 loding效果,若有需要
* @protected
*/
protected handleLoading(): void;
/**
* 模型数据是否发生变更,不传参任意检查任意变更,,传入模型数据path,检查指定变更
* path 'a.b','a[0].a'
* @param path
*/
hasChanged(path?: keyof this['data'] | string): boolean;
private relationKey;
/**
* @param M 类本身,该关系未建立时会关键关联,已建立直接返回关联模型
* @protected
*/
protected relation<T extends Model>(M: new () => T): T;
/**
* @param M 关联模型实例,已设置会覆盖,未设置会设置关联
* @protected
*/
protected relation<T extends Model>(M: T): T;
}
模型缓存配置
import localforage from "localforage";
import type Model from "./Model";
import dayjs from "dayjs";
type ModelCacheConfig = ReturnType<typeof localforage['config']> & {
/**
* 是否使用缓存,默认false
*/
use: boolean;
/**
* 设置过期时间,默认值 () => dayjs().add(10, 'minute'); 10分钟;false 不过期。
*/
expireDate: () => Date | dayjs.Dayjs;
/**
* 账号名(唯一),给定此值时,将以此作为数据库名称。可以返回登录账号的id,用户名等,但应当保证唯一,
* 当不同用户登录时,同一个接口可能返回不同的信息。如我发布的文章。传入此参数可根据账户缓存数据。
* 优先级 account,name。
* 注意,登录接口,或是通用接口(不需要登录的接口),应当设置为 false。
* 常用情况为 项目模型基类 中 account:()=>accountModel.uniName; accountModel 中 account:undefined 。
*/
account?: false | (() => string);
/**
* 是否仅保留一个缓存,默认值 false。启用时,设置缓存数据前会清空当前模型的其他缓存,使其只保留一份。
* 如登录账户应当只有一条记录
*/
single: boolean;
/**
* 过期数据是否使用,默认值 false。
* 启用时,即使缓存数据已过期也会返回并使用。同时也会请求接口更新数据和缓存。
*/
useExpireData: boolean;
/**
* 模型过期自动数据清理间隔,默认值 () => dayjs().add(10, 'minute');10分钟
*/
clearExpireInterval: () => Date | dayjs.Dayjs;
/**
* 应当很少能用到
* 序列化存储缓存,变更会清空数据,默认false。当下载文件时,若需要缓存文件可能需要。当返回结果非常大时,可能需要
*/
serializer: boolean;
};
/**
* 模型的缓存控制
*/
export default class ModelCache<M extends Model> {
constructor(model: M);
private readonly model;
private _localForage;
get localForage(): LocalForage;
private _config;
/**
* 设置如何缓存
* @param options
*/
config(options: Partial<ModelCacheConfig>): M;
config(): ModelCacheConfig;
/**
* 根据是否序列化,拼接仓库名
* @param serializer
* @private
*/
private storeName;
private key;
/**
* 将 Model.query 浅层拼接为key,忽略 空置,复杂数据仅使用类型名
* boolean 转为 01
* @param up
* @private
*/
private getKey;
private convertToKeyValueString;
protected getItem(request: () => void): Promise<any>;
protected setItem(val: any): Promise<unknown>;
/**
* 移除本条缓存,列如某个列表删除一条数据后,需要移除后重新从接口获取数据
*/
removeItem(): Promise<unknown>;
private expireMan;
/**
* 清除模型所有过期数据
*/
clearExpire(): Promise<void>;
private isExpire;
/**
* 清空本模型的所有缓存
*/
clear(): Promise<void>;
/**
* 删除数据仓库(数据表)
*/
delStore(): Promise<void>;
/**
* 清空数据库(注意配置的 name 同 name 会全部清理),会删除掉对应的数据库,请谨慎使用
*/
delDatabase(): Promise<void>;
}
export {};