axiosインターフェース管理をベースにクールな動作を最適化!
この記事の目的は、中規模および大規模のバックエンド プロジェクトのインターフェイス モジュールを最適化し、プロジェクトの通常の動作に影響を与えることなく段階的に更新することです。
機能強化
1. インターフェイス ファイルの作成の簡素化 (インターフェイス モジュールの半自動生成)
2. タスク スケジューリングとロード スケジューリング (インターフェイス レベルでの揺れ防止、ちらつきを防ぐために複数のインターフェイスで 1 つのロードを共有)
3. 無料のインターフェイス プロンプト (プロンプト メッセージは、フロントエンドで制御可能)、バックエンドでも制御可能)
簡易インターフェースファイルの書き込み方法
一部のミッドエンドおよびバックエンド モジュールのインターフェイスについては、基本的にフローの追加、削除、変更、確認、監査の機能です (その他の特殊なインターフェイスについては、当面は説明しません)。バックエンドインターフェースが十分に標準化されている場合、おそらく次のような状況になるでしょう。
import request from "@/utils/request";
// 销售退货列表
export function getSalesReturnList(data) {
return request({
url: "/sales_return/list",
method: "post",
data,});
}
// 保存销售退货
export function saveSalesReturn(data) {
return request({
url: "/sales_return/save",
method: "post",
data,});
}
// 根据Id获取销售退货
export function getSalesReturn(query) {
return request({
url: "/sales_return/get",
method: "get",
params: query,});
}
// 根据Id删除销售退货
export function deleteSalesReturn(data) {
return request({
url: "/sales_return/delete",
method: "post",
data,});
}
// 提交销售退货审核
export function submitSalesReturn(data) {
return request({
url: "/sales_return/submit",
method: "post",
data,});
}
// 审核销售退货
export function auditSalesReturn(data) {
return request({
url: "/sales_return/audit",
method: "post",
data,});
}
// 撤审销售退货
export function revokeAuditSalesReturn(data) {
return request({
url: "/sales_return/withdraw",
method: "post",
data,});
}
// 审核拒绝销售退货
export function rejectSalesReturn(data) {
return request({
url: "/sales_return/reject",
method: "post",
data,});
}
// 作废销售退货
export function discardSalesReturn(data) {
return request({
url: "/sales_return/discard",
method: "post",
data,});
}
これは繰り返しが多すぎるし、インターフェイス関数の名前付けも面倒で、チーム内で標準化するのが難しいと思います。この種のインターフェースファイルがより標準化されるように、それを自動的に生成し、名前を付けることができるようにすることはできますか。
次は方法を考える
上記を前提とすると、ドキュメント モジュールには通常、追加、削除、変更、チェック、送信、無効化、レビュー、レビューの撤回、および拒否の 9 つのインターフェイス メソッドがあります。これらの URL では、前部の sales_return スプライシングは固定されていますが、違いは後部の関数を識別するパス識別子です。また、メソッドは post メソッドと get メソッドに分かれています。
この 9 つのインターフェースを 9 ビットの 2 進数の 9 ビットとみなして、1 が存在、0 が存在しないことを表します。
ビルドの準備としてマップ ファイルを作成できます (以下に示すように)。
export const apiEnum = {
// 查列表2^0
1: {
name: "list",//接口名称
type: "post",//接口方式},
// 查详情2^1
2: {
name: "get",
type: "get",
loading: true,//是否需要loading调度、防抖},
// 删列表 2^2
4: {
name: "delete",
type: "post",},
// 保存 或者 保存且提交2^3
8: {
name: "save",
type: "post",
loading: true,},
// 提交2^4
16: {
name: "submit",
type: "post",
loading: true,},
// 审核2^5
32: {
name: "audit",
type: "post",},
// 撤审2^6
64: {
name: "withdraw",
type: "post",},
// 拒绝2^7
128: {
name: "reject",
type: "post",},
// 作废2^7
256: {
name: "discard",
type: "post",},
};
export const apiFuncModule = {
// 全部
COMMON: 511,
// 增删改查
CURD: 15,
};
1 を渡すと、9 ビットは になります000000001
。これは、クエリ インターフェイスが 1 つだけであることを意味します。15 を渡すと、9 桁は000001111
追加、削除、変更、チェックの 4 つのインターフェイスを表します。等々。
次のステップは、処理関数を完了し、上記の関数を完了することです (次のように)。
import request from "@/utils/request";
import { apiEnum, apiFuncModule } from "@/enum/baseModule/apiEnum";
function useApi(moduleName, code = 511) {
let apiMap = {};
for (let key in apiEnum) {
if ((key & code) == key) {
let obj = apiEnum[key];
//可以按自己习惯来对接口函数命名
let apiName = "api_" + obj.name;
apiMap[apiName] = (data) => {
return request({
url: `/${moduleName}/${obj.name}`,
method: obj.type,
[obj.type == "get" ? "params" : "data"]: data,
loading: obj.loading,
});
};
}}
return apiMap;
}
export { useApi, apiFuncModule as apiType };
上記の手順を完了すると、インターフェイス ファイルは次のように作成できるようになり、9 つのインターフェイスが作成されました。一目でわかるので、変更する場合はパラメータを調整するだけです。
import { useApi } from "@/utils/system/apiGenPlugin";
//code可以不传 ,默认为511
export const API = useApi("sales_return");
//若有其他特殊接口 兼容原始写法 互不影响
export function xxxxx(data) {...
}
使用法
//API集中管理
import { API as SalesReturn } from "@/api/workApi/sale/return";
const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = SalesReturn
//单独使用
import { useApi } from "@/utils/system/apiGenPlugin";
const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = useApi('sales_return')
- SalesReturn.api_save を追加
- DeleteSalesReturn.api_delete
- SalesReturn.api_get の変更
- SalesReturn.api_list を確認する
- SalesReturn.api_audit を監査する
- SalesReturn.api_withdraw の撤回
- SalesReturn.api_discard を破棄する
- SubmitSalesReturn.api_submit
- SalesReturn.api_reject を拒否する
タスクのスケジューリング、ロードのスケジューリング
実際の開発では、インターフェース呼び出しで何らかの処理を行う場合があります。
1. 重複投稿を防ぐため、投稿イベントに対して手ぶれ補正処理を行います。
2. 特定の重要なリソースをロードするときに、ユーザー エクスペリエンスを最適化するためのロード効果があることを望みます。
3. ページのちらつきを防ぐために、読み込み効果を必要とする複数のインターフェイスで同じ読み込みを共有します。
これらの機能は個別に扱うのが面倒で、書き方も人それぞれ異なるため、後のメンテナンスコストがさらに難しくなります。
何もせずに、コードを投稿するだけです。
インターフェーススケジューリングクラス
import { Loading } from "element-ui";
class RequestLock {
// Loading 实例
L_instance = null;
// 接口map
reqMap = new Map();
// 最近一次调用接口时间戳
timestamp = 0;
constructor(timeout = 500) {
// 过渡时间
this.timeout = timeout;}
// 创建任务
put = (id) => {
if (this.reqMap.has(id)) return false;
this._put(id);
return true;};
_put = (id) => {
this.timestamp = new Date().getTime();
this.reqMap.set(id, true);
//开启loading
this.L_instance = Loading.service({
fullscreen: true,
background: "rgba(255, 255, 255, 0.1)",
lock: true,
});};
// 移除任务
del = (id) => {
if (this.reqMap.has(id)) {
this.reqMap.delete(id);
if (this.reqMap.size == 0) {
this._closeLoading();
}
}};
// 清空所有的任务
clearTask = () => {
this.reqMap.clear();
this.L_instance.close();};
//平滑关闭loading
_closeLoading = () => {
let _timestamp = new Date().getTime();
let settime = _timestamp - this.timestamp;
if (settime > this.timeout) {
this.L_instance?.close();
} else {
setTimeout(() => {
this.L_instance?.close();
}, this.timeout - settime);
}};
}
export default RequestLock;
axiosで使用する
これは増分最適化であり、以前のコードに影響を与えることなく機能を追加します。
import { RequestLock } from "@/class/lock";
let loadLock = new RequestLock(500);
//请求拦截
service.interceptors.request.use((config) => {
...
//如果配置中有loading 开启调度
if (config.loading) {
if (!loadLock.put(config.url)) {
return Promise.reject(new Error("repeat request!"));
}
}
...
return config;},(error) => {
...
//如果有错误请求,中止当前调度任务,并清空
loadLock.clearTask();
...
return Promise.reject(error);}
);
//响应拦截
service.interceptors.response.use((response) => {
...
//检查
response.config.loading && loadLock.del(response.config.url);
...},(error) => {
loadLock.clearTask();
return Promise.reject(error);}
);
インターフェースファイルの書き込み
// 根据Id获取销售退货
export function getSalesReturn(query) {
return request({
url: "/sales_return/get",
method: "get",
params: query,
//在这里配置loading为true,开启
loading:true});
}
情報自由化のヒント
データを削除するときに、削除が成功したかどうかを示すポップアップ ボックスが必要になる場合があります。通常、インターフェイスが正常にコールバックしたときにこの関数を追加します。プロンプト ボックスの説明と色を表示するには、ステータスを決定する必要があります。一方、データの一部が削除される場合、ビジネス要件のプロンプトが単純な「削除に成功しました!」だけでなく、他の追加のプロンプトが必要になる場合もあります。たとえば、「ドキュメント xxx は正常に削除されました。時間内に xxxx を処理してください!」などです。この要件は難しいものではありませんが、通信コストと保守コストがかかります。事業内容に何らかの変更があった場合には修正が必要となります。
一方、バックエンドはシステムのビジネス ロジックに近いため、プロンプト機能をバックエンドに任せる方が合理的です。もちろん、フロントエンドも特定のニーズに対応するためにこの機能を保持する必要があります。
import { Message } from "element-ui";
export function responseMsgHandle(res) {
//这里需要后端响应数据格式的配合,MsgType表示提示状态,Msg表示提示描述
let { MsgType, Msg } = res;
if (["success", "warning", "error"].includes(MsgType)) {
Message({
message: Msg,
type: MsgType,
duration: 5 * 1000,
});}
}
使用
import { responseMsgHandle } from "@/utils";
//响应拦截
service.interceptors.response.use((response) => {
...
const res = response.data;
responseMsgHandle(res);
...},(error) => {
...
responseMsgHandle({
MsgType:"error",
Msg:error.message,
});
...
return Promise.reject(error);}
);
要約する
上記の 3 つの単純な最適化ソリューションは、組み合わせて使用することも、個別に使用することもできます。実際のプロジェクトのニーズに応じて変更して使用できます。
基本的に、反復作業の大部分を解決し、メンテナンスコストを削減できます。
やっと
最近、VUE のさまざまな知識をまとめた VUE ドキュメントを見つけ、「Vue 開発のために知っておくべき 36 のヒント」としてまとめました。内容は比較的詳しく、さまざまな知識の解説も充実しています。
必要な友達は下のカードをクリックして受け取り、無料で共有できます