【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 的泛型上。

結語

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

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