在工作至今見過各種形形色色的請求封裝和使用,但是無一例外很多都是無效封裝,或者讓使用者變得更複雜了,打算寫一篇封裝的常見做法所以就有了本文。
下列舉例包括 Axios Fetch UseFetch (Nuxt3)
DX 體驗#
作為團隊內的主導,封裝一個符合 DX 體驗 (開發人員體驗),
是非常有必要的一件事,畢竟你封裝的東西要被其他人大量使用並且照顧到整個項目的應用場景,DX 體驗的位置應該放在 Top。
為此我列出了一下幾點 ...
- 優雅的 API 調用
- 與業務邏輯切割
- 帶有自定義異常處理的能力
- 允許阻止默認行為
- 支持獲得原始數據的能力
- 創建新接口的負擔減少
針對於以上幾點,我們逐一剖析
優雅的 API 調用#
先看案例
/* 1. 文件夾分類的API */
import { getList } from "@/api/demo";
/* 2. 統一以別名導入全部API */
import api from "api";
async function preload(){
try {
/* 以1導入的方式調用 */
const result = await getList();
/* 以2導入的方式調用 */
const result = await api.demo.getList();
// 只要響應成功代碼將會執行到此處
} catch (error){
// 所有的業務code異常和httpStatus異常將會走到此處 如果你需要自己處理異常的話
if(error instanceof Error){
// Toast....
}
}
}
在以上案例中我們區分了兩種導入的方式和使用範例,我們再詳細的深入常見業務比如 分頁
const result = await list();
type C = () => Promise<boolean>
/**
result的數據結構以分頁為例
result = {
pageNum:number
pageSize:number
list:Map<number,T>
to:C // 去指定頁
next:C // 下一頁
back:C // 返回首頁
status:"loading" | "end"
}
*/
那麼這樣我們可以幫其他的業務人員減少在 API 上所耗費的時間和精力同時也不會定義很多沒有必要的變量,你所需要做的就是調用一個 api
接下來我們會展開講如何設計一個這樣的自定義 Hook
api 走向#
針對於以上邏輯我們可以簡單的推出一個具體調用走向
頁面 ---> 業務 API ---> 根請求
根請求 --> 處理 httpStatus -> 處理業務 code ->
--> 包裝分頁 -> 返回
--> 不包裝 -> 返回
開始封裝 以 Axios 為例#
// request.ts
import axios from "axios"
import type { AxiosRequestConfig } from "axios"
/* 創建全局axios實例 */
const axiosiInstance = axios.create({
baseUrl:"https://api.example.com/",
timeOut:6000
});
/* 後端公共響應體 */
export instface Request<T=null>{
code: number;
data: T;
success: boolean;
msg: string | null
}
/* 支持自定義ErrorCode */
export interface ErrorInstall {
[key:string|number]: string;
}
const StatusCode = 200;
export default async function request<R=null>(conf:AxiosRequestConfig,err:ErrorInstall,dialog=true){
/* 在正常的業務邏輯中 總是會要求攜帶各式各樣的自定義header */
/* 在此處clone一份conf */
const config: AxiosRequestConfig = {
// headers放置在上方即可讓config擁有headers默認值
// 在下方的 ...conf 如果有headers就可以覆蓋默認值讓headers始終存在
headers:{},
...conf,
}
/* 在此處進行公共header添加 比如token */
// TODO ....
const {status,data,statusText} = await axiosiInstance<Request<R>>(config);
let error:Error;
/* 針對於httpStatus的處理 */
if(status !== StatusCode) error = new Error(statusText);
/* 如果有業務code則覆蓋httpStatus */
if (data && data.code !== StatusCode && err[data.code]) {
error = new Error(err[data.code]);
}
/* 如果存在error則觸發公共報錯彈框 */
if(error instanceof Error){
if(dialog) showFailToast(error.message);
throw { error, data };
}
// 響應
return data;
}
至此我們就完成了 對於根請求封裝
在下一章我們將會對講解如何對於響應內容做分頁包裝處理