<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于 React 和 Redux 的 API 集成解決方案

          共 10391字,需瀏覽 21分鐘

           ·

          2020-11-11 22:47

          在前端開發(fā)的過程中,我們可能會(huì)花不少的時(shí)間去集成 API、與 API 聯(lián)調(diào)、或者解決 API 變動(dòng)帶來的問題。如果你也希望減輕這部分負(fù)擔(dān),提高團(tuán)隊(duì)的開發(fā)效率,那么這篇文章一定會(huì)對(duì)你有所幫助。
          文章中使用到的技術(shù)棧主要有:
          • react?全家桶

          • TypeScript

          • Rxjs

          文章中會(huì)講述集成 API 時(shí)遇到的一些復(fù)雜場(chǎng)景,并給出對(duì)應(yīng)解決方案。通過自己寫的小工具,自動(dòng)生成 API 集成的代碼,極大提升團(tuán)隊(duì)開發(fā)效率。
          本文的所有代碼都在這個(gè)倉庫:request。自動(dòng)生成代碼的工具在這里:ts-codegen。

          1、統(tǒng)一處理 HTTP 請(qǐng)求

          1.1 、為什么要這樣做?

          我們可以直接通過 fetch 或者 XMLHttpRequest 發(fā)起 HTTP 請(qǐng)求。但是,如果在每個(gè)調(diào)用 API 的地方都采用這種方式,可能會(huì)產(chǎn)生大量模板代碼,而且很難應(yīng)對(duì)一些業(yè)務(wù)場(chǎng)景:
          • 如何為所有的請(qǐng)求添加 loading 動(dòng)畫?

          • 如何統(tǒng)一顯示請(qǐng)求失敗之后的錯(cuò)誤信息?

          • 如何實(shí)現(xiàn) API 去重?

          • 如何通過 Google Analytics 追蹤請(qǐng)求?

          因此,為了減少模板代碼并應(yīng)對(duì)各種復(fù)雜業(yè)務(wù)場(chǎng)景,我們需要對(duì) HTTP 請(qǐng)求進(jìn)行統(tǒng)一處理。

          1.2 如何設(shè)計(jì)和實(shí)現(xiàn)?

          通過 redux,我們可以將 API 請(qǐng)求 「action 化」。換句話說,就是將 API 請(qǐng)求轉(zhuǎn)化成 redux 中的 action。通常來說,一個(gè) API 請(qǐng)求會(huì)轉(zhuǎn)化為三個(gè)不同的 action: request action、request start action、request success/fail action。分別用于發(fā)起 API 請(qǐng)求,記錄請(qǐng)求開始、請(qǐng)求成功響應(yīng)和請(qǐng)求失敗的狀態(tài)。然后,針對(duì)不同的業(yè)務(wù)場(chǎng)景,我們可以實(shí)現(xiàn)不同的 middleware 去處理這些 action。

          1.2.1 Request Action

          redux 的 dispatch 是一個(gè)同步方法,默認(rèn)只用于分發(fā) action (普通對(duì)象)。但通過 middleware,我們可以 dispatch 任何東西,比如 function (redux-thunk) 和 observable,只要確保它們被攔截即可。
          要實(shí)現(xiàn)異步的 HTTP 請(qǐng)求,我們需要一種特殊的 action,本文稱之為?request action?。request action 會(huì)攜帶請(qǐng)求參數(shù)的信息,以便之后發(fā)起 HTTP 請(qǐng)求時(shí)使用。與其他 action 不同的是,它需要一個(gè)?request?屬性作為標(biāo)識(shí)。其定義如下:
          interface IRequestAction {  type: T  meta: {    request: true // 標(biāo)記 request action  };  payload: AxiosRequestConfig; // 請(qǐng)求參數(shù)}

          redux 的 action 一直飽受詬病的一點(diǎn),就是會(huì)產(chǎn)生大量模板代碼而且純字符串的 type 也很容易寫錯(cuò)。所以官方不推薦我們直接使用 action 對(duì)象,而是通過 action creator 函數(shù)來生成相應(yīng)的 action。

          比如社區(qū)推出的 redux-actions,就能夠幫助我們很好地創(chuàng)建 action creator。參考它的實(shí)現(xiàn),我們可以實(shí)現(xiàn)一個(gè)函數(shù) createRequestActionCreator ,用于創(chuàng)建如下定義的 action creator:

          interface IRequestActionCreator {  (args: TReq, extraMeta?: TMeta): IRequestAction;
          TReq: TReq; // 請(qǐng)求參數(shù)的類型 TResp: TResp; // 請(qǐng)求響應(yīng)的類型 $name: string; // request action creator 函數(shù)的名字 toString: () => string; start: { toString: () => string; }; success: { toString: () => string; }; fail: { toString: () => string; };}

          在上面的代碼中,TReq 和 TResp 分別表示?請(qǐng)求參數(shù)的類型?和?請(qǐng)求響應(yīng)的類型。它們保存在 request action creator 函數(shù)的原型上。這樣,通過 request action creator,我們就能迅速知道一個(gè) API 請(qǐng)求參數(shù)的類型和響應(yīng)數(shù)據(jù)的類型。

          const user: typeof getUser.TResp = { name: "Lee", age: 10 };

          對(duì)于 API 請(qǐng)求來說,請(qǐng)求開始、請(qǐng)求成功和請(qǐng)求失敗這幾個(gè)節(jié)點(diǎn)非常重要。因?yàn)槊恳粋€(gè)節(jié)點(diǎn)都有可能觸發(fā) UI 的改變。

          我們可以定義三種特定 type 的 action 來記錄每個(gè)異步階段。也就是我們上面提到的 request start action、request success action 和 request fail action,其定義如下:

          interface IRequestStartAction {  type: T; // xxx_START  meta: {    prevAction: IRequestAction; // 保存其對(duì)應(yīng)的 reqeust action  };}
          interface IRequestSuccessAction { type: T; // xxx_SUCCESS payload: AxiosResponse; // 保存 API Response meta: { prevAction: IRequestAction; };}
          interface IRequestFailAction { type: T; // xxx_FAIL error: true; payload: AxiosError; // 保存 Error meta: { prevAction: IRequestAction; };}

          在上面的代碼中,我們?cè)?request action creator 的原型上綁定了 toString 方法,以及 start、 success 和 fail 屬性。因?yàn)?action type 是純字符串,手寫很容易出錯(cuò),所以我們希望通過 request action creator 直接獲取它們的 type,就像下面這樣:

          `${getData}` // "GET_DATA"`${getData.start}` // "GET_DATA_START"`${getData.success}` // "GET_DATA_SUCCESS"`${getData.fail}`  // "GET_DATA_FAIL"

          1.2.2 Request Middleware

          接下來,我們需要?jiǎng)?chuàng)建一個(gè) middleware 來統(tǒng)一處理 request action。middleware 的邏輯很簡單,就是攔截所有的 request action,然后發(fā)起 HTTP 請(qǐng)求:

          • 請(qǐng)求開始:dispatch xxx_STAT action,方便顯示 loading

          • 請(qǐng)求成功:攜帶 API Response,dispatch xxx_SUCCESS action

          • 請(qǐng)求失敗:攜帶 Error 信息,dispatch xxx_FAIL action

          這里需要注意的是,request middleware 需要「吃掉」request action,也就是說不把這個(gè) action 交給下游的 middleware 進(jìn)行處理。一是因?yàn)檫壿嬕呀?jīng)在這個(gè) middleware 處理完成了,下游的 middleware 無需處理這類 action。二是因?yàn)槿绻掠蔚?middleware 也 dispatch request action,會(huì)造成死循環(huán),引發(fā)不必要的問題。

          1.3 如何使用?

          我們可以通過分發(fā) request action 來觸發(fā)請(qǐng)求的調(diào)用。然后在 reducer 中去處理 request success action,將請(qǐng)求的響應(yīng)數(shù)據(jù)存入 redux store。

          但是,很多時(shí)候我們不僅要發(fā)起 API 請(qǐng)求,還要在?請(qǐng)求成功?和?請(qǐng)求失敗?的時(shí)候去執(zhí)行一些邏輯。

          這些邏輯不會(huì)對(duì) state 造成影響,因此不需要在 reducer 中去處理。比如:用戶填寫了一個(gè)表單,點(diǎn)擊 submit 按鈕時(shí)發(fā)起 API 請(qǐng)求,當(dāng) API 請(qǐng)求成功后執(zhí)行頁面跳轉(zhuǎn)。

          這個(gè)問題用 Promise 很好解決,你只需要將邏輯放到它的 then 和 catch 中即可。然而,將請(qǐng)求 「action化」之后,我們不能像 Promise 一樣,在調(diào)用請(qǐng)求的同時(shí)注冊(cè)請(qǐng)求成功和失敗的回調(diào)。

          如何解決這個(gè)問題呢?我們可以實(shí)現(xiàn)一種類似 Promise 的調(diào)用方式,允許我們?cè)诜职l(fā) request action 的同時(shí)去注冊(cè)請(qǐng)求成功和失敗的回調(diào)。也就是我們即將介紹的 useRequest。

          1.3.1 useRequest:?基于?react?Hooks 和 RXjs?調(diào)用請(qǐng)求

          為了讓發(fā)起請(qǐng)求、請(qǐng)求成功和請(qǐng)求失敗這幾個(gè)階段不再割裂,我們?cè)O(shè)計(jì)了 onSuccess 和 onFail 回調(diào)。類似于 Promise 的 then 和 catch。希望能夠像下面這樣去觸發(fā) API 請(qǐng)求的調(diào)用:

          // 偽代碼
          useRequest(xxxActionCreator, { onSuccess: (requestSuccessAction) => { // do something when request success }, onFail: (requestFailAction) => { // do something when request fail },});

          通過 RxJS 處理請(qǐng)求成功和失敗的回調(diào)

          Promise 和 callback 都像「潑出去的水」,正所謂「覆水難收」,一旦它們開始執(zhí)行便無法取消。如果遇到需要「取消」的場(chǎng)景就會(huì)比較尷尬。雖然可以通過一些方法繞過這個(gè)問題,但始終覺得代碼不夠優(yōu)雅。因此,我們引入了 RxJS,嘗試用一種新的思路去探索并解決這個(gè)問題。

          我們可以改造 redux 的 dispatch 方法,在每次 dispatch 一個(gè) action 之前,再 dispatch 一個(gè) subject$?(觀察者)。

          接著,在 middleware 中創(chuàng)建一個(gè) rootSubject$?(可觀察對(duì)象),用于攔截 dispatch 過來的 subject$,并讓它成為 rootSubject$?的觀察者。rootSubject$?會(huì)把 dispatch 過來的 action 推送給它的所有觀察者。

          因此,只需要觀察請(qǐng)求成功和失敗的 action,執(zhí)行對(duì)應(yīng)的 callback 即可。

          利用 Rx 自身的特性,我們可以方便地控制復(fù)雜的異步流程,當(dāng)然也包括取消。

          實(shí)現(xiàn) useRequest Hook

          useRequest 提供用于分發(fā) request action 的函數(shù),同時(shí)在請(qǐng)求成功或失敗時(shí),執(zhí)行相應(yīng)的回調(diào)函數(shù)。它的輸入和輸出大致如下:

          interface IRequestCallbacks {  onSuccess?: (action: IRequestSuccessAction) => void;  onFail?: (action: IRequestFailAction) => void;}
          export enum RequestStage { START = "START", SUCCESS = "SUCCESS", FAILED = "FAIL",}
          const useRequest = extends IRequestActionCreator"TReq"], T["TResp"]>>( actionCreator: T, options: IRequestCallbacks"TResp"]> = {}, deps: DependencyList = [],) => {
          // ...
          return [request, requestStage$] as [typeof request, BehaviorSubject];};

          它接收 actionCreator 作為第一個(gè)參數(shù),并返回一個(gè) request 函數(shù),當(dāng)你調(diào)用這個(gè)函數(shù)時(shí),就可以分發(fā)相應(yīng)的 request action,從而發(fā)起 API 請(qǐng)求。

          同時(shí)它也會(huì)返回一個(gè)可觀察對(duì)象 requestStage$(可觀察對(duì)象)?,用于推送當(dāng)前請(qǐng)求所處的階段。其中包括:請(qǐng)求開始、成功和失敗三個(gè)階段。

          這樣,在發(fā)起請(qǐng)求之后,我們就能夠輕松地追蹤到它的狀態(tài)。

          這在一些場(chǎng)景下非常有用,比如當(dāng)請(qǐng)求開始時(shí),在頁面上顯示 loading 動(dòng)畫,請(qǐng)求結(jié)束時(shí)關(guān)閉這個(gè)動(dòng)畫。

          為什么返回可觀察對(duì)象 requestStage$?而不是返回 requestStage 狀態(tài)呢?如果返回狀態(tài),意味著在請(qǐng)求開始、請(qǐng)求成功和請(qǐng)求失敗時(shí)都需要去 setState。但并不是每一個(gè)場(chǎng)景都需要這個(gè)狀態(tài)。

          對(duì)于不需要這個(gè)狀態(tài)的組件來說,就會(huì)造成一些浪費(fèi)(re-render)。因此,我們返回一個(gè)可觀察對(duì)象,當(dāng)你需要用到這個(gè)狀態(tài)時(shí),去訂閱它就好了。

          ?options 作為它的第二個(gè)參數(shù),你可以通過它來指定 onSuccess 和 onFail 回調(diào)。onSuccess 會(huì)將 request success action 作為參數(shù)提供給你,你可以通過它拿到請(qǐng)求成功響應(yīng)之后的數(shù)據(jù)。

          然后,你可以選擇將數(shù)據(jù)存入 redux store,或是 local state,又或者你根本不在乎它的響應(yīng)數(shù)據(jù),只是為了在請(qǐng)求成功時(shí)去跳轉(zhuǎn)頁面。

          但無論如何,通過 useRequest,我們都能更加便捷地去實(shí)現(xiàn)需求。

          const [getBooks] = useRequest(getBooksUsingGET, {  success: (action) => {    saveBooksToStore(action.payload.data); // 將 response 數(shù)據(jù)存入 redux store  },});
          const onSubmit = (values: { name: string; price: number }) => { getBooks(values);};

          復(fù)雜場(chǎng)景

          useRequest 封裝了調(diào)用請(qǐng)求的邏輯,通過組合多個(gè) useRequest ,可以應(yīng)對(duì)很多復(fù)雜場(chǎng)景。

          處理多個(gè)相互獨(dú)立的 Request Action

          同時(shí)發(fā)起多個(gè)不同的 request action,這些 request action 之間相互獨(dú)立,并無關(guān)聯(lián)。這種情況很簡單,使用多個(gè) useRequest 即可。

          const [requestA] = useRequest(A);const [requestB] = useRequest(B);const [requestC] = useRequest(C);
          useEffect(() => { requestA(); requestB(); requestC();}, []);
          處理多個(gè)相互關(guān)聯(lián)的 Request Action

          同時(shí)發(fā)起多個(gè)不同的 request action,這些 request action 之間有先后順序。比如發(fā)起 A 請(qǐng)求,A 請(qǐng)求成功了之后發(fā)起 B 請(qǐng)求,B 請(qǐng)求成功了之后再發(fā)起 C 請(qǐng)求。

          由于 useRequest 會(huì)創(chuàng)建發(fā)起請(qǐng)求的函數(shù),并在請(qǐng)求成功之后執(zhí)行 onSuccess 回調(diào)。因此,我們可以通過 useRequest 創(chuàng)建多個(gè) request 函數(shù),并預(yù)設(shè)它們成功響應(yīng)之后的邏輯。就像 RXJS 中「預(yù)鋪設(shè)管道」一樣,當(dāng)事件發(fā)生之后,系統(tǒng)會(huì)按照預(yù)設(shè)的管道運(yùn)作。

          // 預(yù)先創(chuàng)建所有的 request 函數(shù),并預(yù)設(shè) onSuccess 的邏輯const [requestC] = useRequest(C);
          const [requestB] = useRequest(B, { onSuccess: () => { requestC(); },});const [requestA] = useRequest(A, { onSuccess: () => { requestB(); },});
          // 當(dāng) requestA 真正調(diào)用之后,程序會(huì)按照預(yù)設(shè)的邏輯執(zhí)行。
          處理多個(gè)相同的 request action

          同時(shí)發(fā)起多個(gè)完全相同的 request action,但是出于性能的考慮,我們通常會(huì)「吃掉」相同的 action,只有最后一個(gè) action 會(huì)發(fā)起 API 請(qǐng)求。也就是我們前面提到過的 API 去重。但是對(duì)于 request action 的回調(diào)函數(shù)來說,可能會(huì)有下面兩種不同的需求:

          1. 每個(gè)相同 request action 所對(duì)應(yīng)的 onSuccess/onFail 回調(diào)在請(qǐng)求成功時(shí)都會(huì)被執(zhí)行。

          2. 只執(zhí)行真正發(fā)起請(qǐng)求的這個(gè) action 所對(duì)應(yīng)的 onSuccess/onFail 回調(diào)。

          對(duì)于第一個(gè)場(chǎng)景來說,我們可以判斷 action 的 type 和 payload 是否一致,如果一致就執(zhí)行對(duì)應(yīng)的 callback,這樣相同 action 的回調(diào)都可以被執(zhí)行。對(duì)于第二個(gè)場(chǎng)景,我們可以從 action 的 payload 上做點(diǎn)「手腳」,action 的 payload 放置的是我們發(fā)起請(qǐng)求時(shí)需要的 request config,通過添加一個(gè) UUID,可以讓這個(gè)和其他 action「相同」的 action 變得「不同」,這樣就只會(huì)執(zhí)行這個(gè) request action 所對(duì)應(yīng)的回調(diào)函數(shù)。

          組件卸載

          通常我們會(huì)使用 Promise 或者 XMLHttpRequest 發(fā)起 API 請(qǐng)求,但由于 API 請(qǐng)求是異步的,在組件卸載之后,它們的回調(diào)函數(shù)仍然會(huì)被執(zhí)行。這就可能導(dǎo)致一些問題,比如在已卸載的組件里執(zhí)行 setState。

          組件被卸載之后,組件內(nèi)部的邏輯應(yīng)該隨之「銷毀」,我們不應(yīng)該再執(zhí)行任何組件內(nèi)包含的任何邏輯。利用 RxJS,useRequest 能夠在組件銷毀時(shí)自動(dòng)取消所有邏輯。換句話說,就是不再執(zhí)行請(qǐng)求成功或者失敗的回調(diào)函數(shù)。

          2. 存儲(chǔ)并使用請(qǐng)求響應(yīng)的數(shù)據(jù)

          對(duì)于 API Response 這一類數(shù)據(jù),我們應(yīng)該如何存儲(chǔ)呢?由于不同的 API Response 數(shù)據(jù)對(duì)應(yīng)用有著不同的作用,因此我們可以抽象出對(duì)應(yīng)的數(shù)據(jù)模型,然后分類存儲(chǔ)。就像我們收納生活用品一樣,第一個(gè)抽屜放餐具,第二個(gè)抽屜放零食......

          按照數(shù)據(jù)變化的頻率,或者說數(shù)據(jù)的存活時(shí)間,我們可以將 API response 大致歸為兩類:

          一類是變化頻率非常高的數(shù)據(jù),比如排行榜列表,可能每一秒都在發(fā)生變化,這一類數(shù)據(jù)沒有緩存價(jià)值,我們稱之為臨時(shí)數(shù)據(jù)(temporary data)。臨時(shí)數(shù)據(jù)用完之后會(huì)被銷毀。

          另一類是不常發(fā)生變化的數(shù)據(jù),我們稱之為實(shí)體數(shù)據(jù)(entity),比如國家列表、品牌列表。這一類數(shù)據(jù)很多時(shí)候需要緩存到本地,將它們歸為一類更易于做數(shù)據(jù)持久化。

          2.1 useTempData

          2.1.2 背景

          通過 useRequest 我們已經(jīng)能夠非常方便的去調(diào)用 API 請(qǐng)求了。但是對(duì)于大部分業(yè)務(wù)場(chǎng)景來說,還是會(huì)比較繁瑣。試想一個(gè)非常常見的需求:將 API 數(shù)據(jù)渲染到頁面上。我們通常需要以下幾個(gè)步驟:

          Step1: 組件 mount 時(shí),dispatch 一個(gè) request action。這一步可以通過 useRequest 實(shí)現(xiàn)。

          Step2: 處理 request success action,并將數(shù)據(jù)存入 store 中。

          Step3: 從 store 的 state 中 pick 出對(duì)應(yīng)的數(shù)據(jù),并將其提供給組件。

          Step4: 組件拿到數(shù)據(jù)并渲染頁面。

          Step5: 執(zhí)行某些操作之后,用新的 request 參數(shù)重新發(fā)起請(qǐng)求。

          Step6: 重復(fù) Step2、Step3、Step4。

          如果每一次集成 API 都要通過上面的這些步驟才能完成,不僅會(huì)浪費(fèi)大量時(shí)間,也會(huì)生產(chǎn)大量模板代碼。并且,由于邏輯非常地分散,我們無法為它們統(tǒng)一添加測(cè)試,因此需要在每個(gè)使用的地方單獨(dú)去測(cè)。可想而知,開發(fā)效率一定會(huì)大打折扣。

          為了解決這個(gè)問題,我們抽象了 useTempData。之前也提到過 temp data 的概念,其實(shí)它就是指頁面上的臨時(shí)數(shù)據(jù),通常都是「閱后即焚」。我們項(xiàng)目上通過 API 請(qǐng)求獲取的數(shù)據(jù)大部分都是這一類。useTempData 主要用于在組件 mount 時(shí)自動(dòng)獲取 API 數(shù)據(jù),并在組件 unmount 時(shí)自動(dòng)銷毀它們。

          2.1.3 輸入和輸出

          useTempData 會(huì)在組件 mount 時(shí)自動(dòng)分發(fā) request action,當(dāng)請(qǐng)求成功之后將響應(yīng)數(shù)據(jù)存入 redux ?store,然后從 store 提取出響應(yīng)數(shù)據(jù),將響應(yīng)數(shù)據(jù)提供給外部使用。當(dāng)然,你也可以通過配置,讓 useTempData 響應(yīng)請(qǐng)求參數(shù)的變化,當(dāng)請(qǐng)求參數(shù)發(fā)生變化時(shí),useTempData 會(huì)攜帶新的請(qǐng)求參數(shù)重新發(fā)起請(qǐng)求。

          其核心的輸入輸出如下:

          export const useTempData = extends IRequestActionCreator"TReq"], T["TResp"]>>(  actionCreator: T,  args?: T["TReq"],  deps: DependencyList = [],) => {  // ...  return [data, requestStage, fetchData] as [    typeof actionCreator["TResp"],    typeof requestStage,    typeof fetchData,  ];};

          它接收 actionCreator 作為第一個(gè)參數(shù),用于創(chuàng)建相應(yīng)的 request action。當(dāng)組件 mount 時(shí),會(huì)自動(dòng)分發(fā) request action。args 作為第二個(gè)參數(shù),用于設(shè)置請(qǐng)求參數(shù)。deps 作為第三個(gè)參數(shù),當(dāng)它發(fā)生變化時(shí),會(huì)重新分發(fā) request action。

          同時(shí),它會(huì)返回 API 響應(yīng)的數(shù)據(jù) data、表示請(qǐng)求當(dāng)前所處階段的 requestStage ?以及用于分發(fā) request action 的函數(shù) fetchData 。

          使用起來也非常方便,如果業(yè)務(wù)場(chǎng)景比較簡單,集成 API 就是一行代碼的事:

          const [books] = useTempData(getBooksUsingGET, { bookType }, [bookType]);
          // 拿到 books 數(shù)據(jù),渲染 UI

          2.1.4 實(shí)現(xiàn)思路

          useTempData 基于 useRequest 實(shí)現(xiàn)。在組件 mount 時(shí)分發(fā) request action,然后在請(qǐng)求成功的回調(diào)函數(shù) onSuccess 中再分發(fā)另一個(gè) action,將請(qǐng)求響應(yīng)的數(shù)據(jù)存入 redux store。

          const [fetchData] = useRequest(actionCreator, {  success: (action) => {    dispatch(updateTempData(groupName, reducer(dataRef.current, action))),  },});
          useEffect(() => { fetchData(args as any);}, deps);

          2.1.5 組件卸載

          當(dāng)組件卸載時(shí),如果 store 的 state 已經(jīng)保存了這個(gè) request action 成功響應(yīng)的數(shù)據(jù),useTempData 會(huì)自動(dòng)將它清除。發(fā)起 API 請(qǐng)求之后,如果組件已經(jīng)卸載,useTempData 就不會(huì)將請(qǐng)求成功響應(yīng)的數(shù)據(jù)存入 redux store。

          2.2 useEntity

          基于 useTempData 的設(shè)計(jì),我們可以封裝 useEntity, 用于統(tǒng)一處理 entity 這類數(shù)據(jù)。這里不再贅述。

          3. 自動(dòng)生成代碼

          利用代碼生成工具,我們可以通過 swagger 文檔自動(dòng)生成 request action creator 以及接口定義。并且,每一次都會(huì)用服務(wù)端最新的 swagger json 來生成代碼。這在接口變更時(shí)非常有用,只需要一行命令,就可以更新接口定義,然后通過 TypeScript 的報(bào)錯(cuò)提示,依次修改使用的地方即可。

          同一個(gè) swagger 生成的代碼我們會(huì)放到同一個(gè)文件里。在多人協(xié)作時(shí),為了避免沖突,我們會(huì)將生成的 request action creator 以及接口定義按照字母順序進(jìn)行排序,并且每一次生成的文件都會(huì)覆蓋之前的文件。因此,我們?cè)陧?xiàng)目上還硬性規(guī)定了:生成的文件只能自動(dòng)生成,不能夠手動(dòng)修改。

          4. 最后

          自動(dòng)生成代碼工具為我們省去了很大一部分工作量,再結(jié)合我們之前講過的 useRequest、useTempData 和 useEntity,集成 API 就變成了一項(xiàng)非常輕松的工作。

          本文完~


          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰永久在线 | 亚洲乱妇| 成人免费网站在线 | 豆花淫荡视频 | 久久综合8 |