1. 程式人生 > >基於 Fetch 的請求封裝

基於 Fetch 的請求封裝

原生 fetch 請求失敗後(如無網路)狀態會變成 reject 走 .catch 。絕大多數情況下業務場景只需要給個 toast 等簡單處理。每個請求都 .catch 會顯得格外繁瑣,並且如果不 .catch, .then 裡面的後續處理不會觸發,可能會導致邏輯中斷。 基於上述情況,可以封裝公共請求方法處理異常情況,返回固定格式 { code, data, massage }, 只需在 .then 裡面獲取資料並處理。 ## 目標 1. 保留 fetch 語法不變 2. 返回 promise,且狀態一定變成 resolve,返回固定資料格式 { code, data, message } 3. 給 url 動態新增域名 4. 預設失敗彈出 toast,且提供開關 5. 預設請求中 loading,且提供開關 ## 實現 實現如下 ```ts /** * 返回 Promise({ code, data, message}); */ import { message } from 'antd'; import { dispatch } from '../index'; import { startLoading, endLoading } from '../store/globalSlice'; // 在 config/env.js 中設定 export const origin = process.env.ORIGIN; interface MyInit extends RequestInit { noToastError?: boolean; // 預設false noLoading?: boolean; // 預設false } // 請求返回統一格式 export class Response { code: number; data: any; message: string | null; constructor(code: number, data: any, message: string | null) { this.code = code; this.data = data; this.message = message; } } export default (input: RequestInfo, myInit?: MyInit) => { const init: MyInit = myInit || {}; // url 動態新增 origin if (typeof input === 'string') { if (!/https?:\/\//.test(input)) { input = origin + input; } } // 開始 loading if (!init?.noLoading) { dispatch(startLoading()); } // 請求 return fetch(input, { headers: { 'Content-Type': 'application/json', }, ...init, }) .then((resp) => { // code 不為 2xx if (!/^2\d{2}$/.test(String(resp.status))) { return new Response(resp.status, null, null); } return resp .json() .then((json) => { // toast錯誤資訊 if (json.code !== 0) { message.error(json.message); } // 退出登陸 if (json.code === 20001) { // logout(); // window.location.pathname = '/login'; } // 請求成功的資料 return json; }) .catch((err) => { // 不是 json 格式 return new Response(999998, null, 'data is not json.'); }); }) .catch((err) => { // 請求傳送失敗 message.error('您的網路可能不通,請確認後重試'); return new Response(999999, null, err); }) .finally(() => { // 結束 loading if (!init?.noLoading) { dispatch(endLoading()); } }); }; ``` 目前只完成了對 json 資料的處理。 其中 loading 部分用了 redux 和 @reduxjs/toolkit,用一個變數記錄當前請求個數,控制實際 loading 開始和結束,實現如下: ```ts let count = 0; export const startLoading = () => (dispatch: any) => { if (count === 0) { dispatch(setLoding(true)); } count++; }; export const endLoading = () => (dispatch: any) => { count--; if (count === 0) { dispatch(setLoding(false)); } }; ``` ## 使用 ```js import myfetch from './myfetch.ts'; myfetch('/xxx/xxx') .then(res => console.log(res)) myfetch('https://www.xxx.com/xxx/xxx') .then(res => console.log(res)) myfetch('/xxx/xxx', { methods: 'POST', body: JSON.stringify({}), }) .then(res => console.log(res)) myfetch('/xxx/xxx', { methods: 'POST', body: JSON.stringify({}), noToastError: true, noLoading: true, }) .then(res => console.log(res)) ```
[whosmeya.com](https://www.whosmey