【gRPC】封裝前端網路請求的核心思想 - TS版

語言: CN / TW / HK

highlight: vs2015

導讀

本文是個短篇,主要解釋一下封裝網路請求的設計思想,為接下來 gRPC 的封裝做理論鋪墊。會從一個簡單場景出發,來介紹封裝的設計思想。

場景

實現一個最簡單的場景: url | method | params | response | remarks -|-|-|-|- /test | POST | { name: string } | { message: string } | 介面功能:入參為 {name: 'xxx'},則返回值為 {message: 'Hello, xxx'}

程式碼實現

傳統的 RESTful 用 express + fetch 實現方式如下:

服務端

```js // by express const express = require('express'); const app = express();

app.use(express.json({type: 'application/json'}));

app.post("/test", function (req, res, next) { / 約定好 body.name 是個 string / res.send({ message: "Hello," + req.body.name }); });

app.listen(3000); ```

客戶端

程式碼實現分了封裝宣告呼叫三層,詳細分析見後文,先看程式碼實現:

封裝

request.ts - 統一處理初始化、格式化、異常等邏輯; ts // request.ts export const apiPost = <R>( url: string, data: Record<string, unknown> ): Promise<R> => fetch(url, { method: "POST", body: JSON.stringify(data), headers: new Headers({ "Content-Type": "application/json", }), }) .then((res) => { if (res.status === 200) { return res.json(); } throw new Error(res.statusText); }) .catch((error) => { console.log(error); });

宣告

services.ts - 使用“封裝”後的基礎函式,實現業務函式,並加入引數與返回值的 TS 宣告; ```ts // services.ts import { apiPost } from "./request";

export const sayHello = (txt: string) => { return apiPost<{ message: string }>("/test", { txt }); }; ```

呼叫

index.ts - 使用“宣告”的業務函式,此時 TS 已經起到了校驗和自動提示的作用。 ts sayHello("world").then((res) => { console.log(res.message); // Hello, world });

解析

我們關注客戶端即可,上文將程式碼分成了封裝、宣告、呼叫 3 個模組,為什麼呢?我們從後往前說。

【呼叫】最重要的是使用起來要足夠方便,要做到極簡。可以從上文看到,這部分沒有任何多餘的程式碼,但是又能夠享受到 TS 的便利。更極端點說,如果未來從 ts 切回 js,此處的程式碼都不需要做任何改動。讓

【宣告】“呼叫”部分之所以能夠做到極簡,是因為“宣告”部分將需要宣告的 TS 全部都聲明瞭。可以看到每個函式都需要宣告入參和出參,所以這部分最重要的就是 TS 宣告。當然,此處 TS 宣告的方便與否,是由更底層的“封裝”部分決定的。

【封裝】最核心的目標就是底層解耦。即無論底層用 fetchaxios 甚至 gRPC 實現,對外暴露的都是 apiPost 函式,使用方法也不變,這樣能夠充分保證程式碼的魯棒性與拓展性。除此之外,上面也提到了,此部分決定了“宣告”層是否便利,所以還是很考驗設計和抽象能力的,尤其是在處理各種 TS 的泛型上。

結語

總之,關注兩頭即可,一個是做到底層解耦,一個是做到使用極簡。不管怎麼拆分,拆幾層,只要能做到“兩頭極致”就可以了。

在激烈競爭中,取勝的系統在最大化或者最小化一個或幾個變數上會走到近乎荒謬的極端。——《窮查理寶典》