バイトで使用されるコードは自動的に生成されます

バックグラウンド

インターフェイス定義がある場合、フロントエンドとバックエンドの両方がこれに基づいて対応するコードを生成できるため、フロントエンドとバックエンド間の通信コストが削減されるだけでなく、インターフェイスの効率も向上します。研究開発。

Byte内のRPC定義は主にthriftをベースに実装されていますが、thriftではデータ構造や関数を定義しているので、フロントエンド用のインターフェース定義として利用できるのでしょうか?インターフェース定義として使用できる場合、インターフェース定義を介してインターフェースを要求するコードを自動生成することはできますか? 答えは「はい、Byte 内で複数の thrift ベースのコード生成ツールが派生しています。この記事では主に thrift を通じてフロントエンド インターフェイス呼び出しのコードを生成する方法を紹介します。」

インターフェースの定義

インターフェース定義はその名の通りインターフェースを定義するための言語ですが、バイト単位で広く使われている thrift は基本的にインターフェース定義の要件を満たしているので、thrift をそのままインターフェース定義とみなしても良いでしょう。

Thrift は、言語をまたいだリモート プロシージャ コール (RPC) フレームワークです。Typescript に慣れている場合は、その構造は非常に単純に見えるはずです。例を見てみましょう。

namespace go namesapce

// 请求的结构体
struct GetRandomRequest {
    1: optional i32 min,
    2: optional i32 max,
    3: optional string extra
}

// 响应的结构体
struct GetRandomResponse {
    1: optional i64 random_num
}

// 定义服务
service RandomService {
   GetRandomResponse GetRandom (1: GetRandomRequest req)
}
复制代码

例のサービスは機能の集合と考えることができ、各機能はインターフェースとみなすことができます。RESTful インターフェイスの場合、インターフェイス パス (/getUserInfo など) とパラメーター (クエリ パラメーター、本体パラメーターなど) を定義する必要があることは誰もが知っており、これらの追加情報は thrift アノテーション を通じて表現できます

namespace go namesapce

struct GetRandomRequest {
    1: optional i32 min (api.source = "query"),
    2: optional i32 max (api.source = "query"),
    3: optional string extra (api.source = "body"),
}

struct GetRandomResponse {
    1: optional i64 random_num,
}

// Service
service RandomService {
   GetRandomResponse GetRandom (1: GetRandomRequest req) (api.get = "/api/get-random"),
}
复制代码

api.sourceこれは、パラメータの位置を指定するために使用され、queryクエリ パラメータであることを示し、bodyボディ パラメータapi.get="/api/get-random"であることを示し、インターフェイス パスが /api/get-random であり、リクエスト メソッドが GET であることを示します。

タイプスクリプトの生成

上記のインターフェイス定義はすでにあるので、対応する Typescript を作成する準備ができているはずです。コードを一緒に見てみましょう。

interface GetRandomRequest {
    min: number;
    max: number;
    extra: string;
}

interface GetRandomResponse {
    random_num: number;
}

async function GetRandom(req: GetRandomRequest): Promise<GetRandomResponse> {
    return request<GetRandomResponse>({
        url: '/api/get-random',
        method: 'GET',
        query: {
            min: req.min,
            max: req.max,
        },
        body: {
            extra: req.extra,
        }
    });
}

复制代码

Typescript を生成した後は、生成されたコードがどのようになるかを気にする必要はなく、 を直接呼び出すGetRandomだけです。

建築デザイン

要实现基于 thrift 生成代码,最核心的架构如下:

image.png 因为 thrift 的内容我们不能直接拿来用,需要转化成中间代码(IR),这里的中间代码通常是 json、AST 或者自定义的 DSL。如果中间代码是 json,可能的结构如下:

{
    name: 'GetRandom',
    method: 'get',
    path: '/api/get-random',
    req_schema: {
        query_params: [
            {
                name: 'min',
                type: 'int',
                optional: true,
            },
            {
                name: 'max',
                type: 'int',
                optional: true,
            }
        ],
        body_params: [
            {
                name: 'extra',
                type: 'string',
                optional: true,
            }
        ],
        header_params: [],
    },
    resp_schema: {
        header_params: [],
        body_params: [],
    } 
}
复制代码

为了保持架构的开放性,我们在核心链路上插入了 PrePlugin 和 PostPlugin,其中 PrePlugin 决定了 thrift 如何转化成 IR,PostPlugin 决定 IR 如何生成目标代码。

这里之所以是「目标代码」而不是「Typescript 代码」,是因为我希望不同的 PostPlugin 可以产生不同的目标代码,比如可以通过 TSPostPlugin 生成 Typescript 代码,通过 GoPostPlugin 生成 go 语言的代码。

总结

代码生成这块的内容还有很多可以探索的地方,比如如何解析 thrift?是找第三方功能生成 AST 还是通过 pegjs 解析成自定义的 DSL?多文件联编如何处理、字段名 case 如何转换、运行时类型校验、生成的代码如何与 useRequest 或 ReactQuery 集成等。

thrift 其实可以看成接口定义的具体实现,如果 thrift 不满足你的业务场景,也可以自己实现一套类似的接口定义语言;接口定义作为前后端的约定,可以降低前后端的沟通成本;代码生成,可以提升前端代码的质量和研发效率。

如果本文对你有启发,欢迎点赞、关注、留言交流。

おすすめ

転載: juejin.im/post/7220054775298359351