“
本文首发于我的博客,转载请注明出处
”
网上时不时就能看到一些求助帖,粗心是前后端集成所产生的一些争执,其实集成的时候如果能有一些”标准“,这件事件能够很简略。
技术栈
本文跟技术栈强相干,然而实践上从外面抽离进去的方法论能够实际在任意的技术栈上,只须要投入一点点工夫和精力写一个小工具就好了,上面是本文所用到的一些技术栈:
- React
- TypeScript
- SWR
- axios
- 后端应用 Swagger 作为 API 文档
- @openapi-integration/swr-request-generator
swr-request-generator
其中最初一项就是下面提到的须要本人去实现的小工具,这里我是把组里之前给 redux 用的工具改了一版,先是从 swagger2.0 降级到 openAPI 规范,而后改为生成 SWR 的代码而不是 redux 的,具体工具怎么应用能够看我的 github,外面有具体的 readme 和 example。这个工具尽管简略,却是前后端集成不可或缺的重要组成部分,稍后咱们会讲到。
SWR
而后 SWR 是一个很酷的用于获取远端数据的一个基于 React hooks 的申请库。它实现了 HTTP 标准中的 stale-while-revalidate
,即其会先应用 catch 中已存在的 data 先渲染页面,而后发送新的申请去验证 catch 中的 data 是否为最新,如果曾经是最新则什么都不做,如果不是最新则更新本地的 catch 而后应用最新的 catch 从新渲染页面。
这样做的益处是能够极大地晋升用户体验,用户在反复浏览同一页面时,如果页面数据更新不频繁则没有任何等待时间。在新我的项目上应用几个月后,我欣慰地发现 SWR 登上了 ThoughtWorks 最新一期的技术雷达,和 Recoil、Svelte 一起临时位于评估象限。
SWR in tech radar
不过技术雷达也切中时弊地指出了 SWR 的缺点:
“
咱们的开发者在应用 SWR 时取得了很好的开发体验,并且因为数据总是显示在屏幕上,从而显著晋升用户体验。然而,咱们揭示团队,只有当应用程序返回过期数据是适合的时候,能力应用 SWR 缓存策略。要留神,HTTP 通常要求缓存要用最新的响应返回给申请,只有在须要_十分谨慎的场景_下,才会容许返回过期的响应数据。
”
也就是说,如果你的坏境对数据更新的要求极高,须要实时拿到最新的数据的话,不适宜应用 SWR。
TypeScript
TypeScript 当初简直成为了一门前端必上的技术栈,相比 JavaScript,其提供的类型零碎可能保障程序员们常犯的”低级谬误“在写代码时就裸露进去。
但也是因为其类型零碎,咱们在集成后端 API 时须要写一堆麻烦的接口类型,比方一个 request 的参数,这个 response 的数据类型等等。
而这些接口类型其实是后端定义的,咱们其实是在依赖后端写的接口来写类型。这就很难堪了,后端改了接口,前端也得跟着改类型,这多麻烦,前面咱们会一起解决这个问题。
为什么前后端集成问题频出?
要解决一个问题,那么咱们首先应该做的是想分明为什么会呈现这样的问题。
首先咱们来回顾一下惯例的开发过程:拿到需要,前后端探讨出接口的各种参数,开始写代码,这两头可能后端 (前端) 发现有问题,而后改接口,而后差不多写好的接口得从新改,改类型,改参数。
这是惯例的,不惯例的呢?后端本人定好接口的参数,而后通知你他要啥给你啥,而后你依照他给的写,写好发现不能用,去找他,他说接口改了,你从新改下。那还能咋说,只能网上对线了。
说白了就是,前后端探讨好的货色可能会变(甚至都没有通过探讨,由单方面间接决定好了),变了之后因为各种各样的起因没能及时同步信息,即便及时同步了,改接口代码也是一件烦人的事件。
咋办?
从契约测试开展
首先,咱们须要搞清楚什么是契约测试?
契约测试,又叫”消费者驱动的契约测试“(Consumer-Driven Contracts,简称 CDC)。其中有两个角色,一个消费者,一个生产者。由消费者提供一份本人的”需要清单“(契约,约定好 request 和 response),而后生产者依据“清单”进行相应实现。之后单方依赖于这份契约进行测试和实现。
其实契约测试将依赖单方做了一个相似于解耦的操作,从消费者依赖于生产者变成单方依赖于消费者提供的契约。咱们是不是能够使用这个思维呢?
其实大多数我的项目里曾经有一份契约了,没错,就是 swagger。只不过这份契约是生产者提供的,而且由生产者决定下面有啥,消费者没法决定。
然而其实咱们能够将这份契约做一个转换。swagger 上通常会给出 API 的详细信息,包含 request 的参数和 response 长什么样子。而且其实 swagger 只是一份 json 文件,咱们所看到的 swagger-ui 是后端的 lib 主动生成的。那么咱们是不是能够拿到这份 json 文件。解析后间接生成前端须要的 request 和类型呢?
这样带来了几个益处:
- 前端写代码能够不必一个个写 API 了,所有的 url, request params, request method, response type 等等信息咱们都能够通过 swagger 主动生成
- 后端接口改了?没问题,2 秒钟更新好最新的接口代码,而后 TypeScript 的益处就体现进去了,依据新的接口类型改参数就好了
- 生成的代码有问题?那就是后端的问题,你的实现和你本人提供的契约不统一,要么你实现不对,要么你契约不对
上代码
说了这么多都是实践,谁晓得成果呢?没关系,咱们间接上代码,上面所有的代码都能够在链接外面找到。
首先看一下这个 example 的目录:
“
. |____swagger | |____openAPI.json |____types.ts |____request | |____api.ts | |____useRequest.ts | |____client.ts |____page.tsx
”
其中 swagger 文件夹下是我从网上生成的 openAPI 标准的 swagger 文档。
request 文件夹下有三个文件:
- api.ts 这个文件就是通过 @openapi-integration/swr-request-generator 依据下面的 swagger 文档生成的,外面蕴含 API 接口办法和相应的 request params 和 response 的类型定义
- client.js 外面只有一个单纯的 axios client instance,用于发送申请
- useRequest 外面我将 SWR 和 axios 封装起来以供 api.ts 这个文件调用
这货色怎么用呢?
首先当然是装置这个包 npm i @openapi-integration/swr-request-generator -D
而后在我的项目的根目录增加一个配置文件,外面能够配置生成文件的输入目录,文件名,须要提前引入的办法,从哪里拿到 swagger 文件等(具体能够看 readme)
而后跑一下 npm run ts-codegen
,就会在你配置的相应生成对应的 API 文件。
比方上面这个办法就是生成的:
“import {ISWRConfig, useRequest} from “./useRequest”;
import {IResponseError} from “../types”;
import {client} from “./client”;
export const useDownloadUsingGetRequest = (
{
id,
}: {
id: string;
},
SWRConfig?: ISWRConfig<IResource, IResponseError>,
) =>
useRequest<IResource, IResponseError>(
{
url: /${id}
,
method: “get”,
},
SWRConfig,
);
export interface IResource {
description?: string;
file?: IFile;
filename?: string;
inputStream?: IInputStream;
open?: boolean;
readable?: boolean;
uri?: IUri;
url?: IUrl;
}
“
而你只须要在调用的时候间接:
`const = {data, error} = useDownloadUsingGetRequest({id: “id”})
`
如果后端改了代码,这个接口不要 id 了,要 name,你只须要从新跑一次npm run ts-codegen
,而后这个办法就会更新,TypeScript 就会报错,通知你这里要的是 name 不是 id,如果 response 的构造也改了,那么所生成的文件外面的 response 的类型也会跟着变,TypeScript 仍然会揭示你 response 构造的扭转。
这样一来接口无论怎么变其实前端都不在意,顶多就是跑一遍命令,从新穿个参数就好了,大大晋升开发效率,升高内耗。