搭建先后端之桥

随着先后端分离,开发的门槛下降了,咱们再也不要求团队中的每一个开发都是全栈工程师,这样更容易找到项目的合适人选。团队也划分红了前端和后端两个团队。前端负责消费 API 并展现页面,后端负责提供 API。这两个团队能够并行开发互不影响,大大提高了效率。虽然先后端分离解决了不少问题,但同时也带来了新的困扰。前端

先后端分离带来的困扰

沟通成本

先后端成为两个独立团队以后,协做的问题便随之而来。经过什么来协做呢?契约。简单来讲,就是预先定义好精准的接口,好比接口的 URL,包含哪些参数、返回值,每一个值的类型,是否为空等等。定义好以后,先后端就按照契约进行开发。可是在实际场景中,却常常出现问题。git

举个真实的例子。有一次,后端在重构时修改了一个字段名,同时也修改了契约测试,可是却忘了告诉前端。因为这个页面的使用频率不高,前端也工做在别的地方,所以那个页面挂了许久都没有人发现。这些隐藏 Bug 会给咱们的应用带来隐患,同时也会增长开发的负担。github

在实际开发过程当中,保证人人都遵照契约是一个很困难的事情,由于人均可能会犯错。typescript

大量的模板代码

即使团队中全部人都能严格遵循契约,但集成 API 仍旧是个苦力活。我须要定义请求的 URL、Method、参数、参数类型以及返回值类型等等。因而项目中就充斥着下面这样的模板代码:json

interface ICreateBookRequestData {
 bookId: string;
 category: string;
 date: string;
 createdBy: string;
}

interface IBook {
 id: string;
 author: string;
 name: string;
 price: string;
 publishDate: string;
 publishVendor?: string;
}

export const createBook = createRequestAction<ICreateBookRequestData, IBook>(
 "@@books/createBook",
 (data) => ({
   url: "/books/book",
   method: "PUT",
   data,
 }),
);

在多人协做时,为了减小冲突,咱们一般会按照业务场景,将请求相关的代码存储到不一样文件。好比 login.api.ts 存放登陆相关的请求代码,account.api.ts 中存放帐户相关的请求代码。可是这会形成另外一个问题,就是类型的重复定义和定义不一致的问题。后端

在上面的代码中,咱们定义了请求响应数据的类型:IBook。对于后端来讲,这个数据类型是可复用的,其余接口也可使用它。可是对于前端来讲,很难肯定这个数据类型在哪些地方被使用了。所以在不一样的文件中,可能会重复定义相同的类型。因为不一样的人对同一个数据的类型的理解可能不一致,还会形成定义不一致的问题。好比在 A 文件的 IBook 中咱们定义 publishVendor 是一个可选的属性,而在 B 文件中咱们可能再次定义 IBook 并把 publishVendor 定义为一个必需的属性。以下所示:api

interface IBook {
  id: string;
  author: string;
  name: string;
  price: string;
  publishDate: string; 
  publishVendor: string;
}

链接先后端

为了解决上面的问题,咱们实现了一个自动化工具,将割裂的前端和后端从新链接起来。简单来讲,就是经过 Swagger JSON 自动生成调用 API 所需的代码以及类型定义。服务器

OpenAPI 规范(之前称为 Swagger 规范)为 RESTful API 定义了一个与语言无关的标准接口,容许人和计算机发现和理解服务的功能,而无需访问源代码、文档或开发者工具。Swagger 是一套围绕 OpenAPI 规范构建的开源工具,能够帮助咱们生成、描述、调用和可视化 RESTful 风格的服务。

有了自动化工具以后,只须要在终端中执行一行命令,就能马上生成项目中全部 API 相关的代码以及类型定义。这样就不用再写模板代码了,节省了不少时间。同时,因为全部代码都是经过 Swagger JSON 生成的,当接口发生变更时,咱们不用再去查看文档或者询问后端修改了什么,只须要经过命令就能知道哪些接口发生了变化并自动更新对应的前端代码。由于全部代码都是自动生成的,重复定义的问题也就不存在了。对于简单业务场景来讲,集成 API 就是一行代码的事:前后端分离

// getBooksUsingGET 方法由工具自动生成, useTempData 会发起 HTTP 请求并返回响应数据

const [books] = useTempData(getBooksUsingGET, { bookType }, [bookType]);

// 拿到 books 数据,渲染 UI

落地和优化

当我实现完这个工具的第一个可用版本,准备在项目中推行时,却发现还有一些问题亟待解决:工具

Q: 如何保证生成代码的一致性?

A: 每次运行命令都会从远程服务器上去获取 Swagger JSON,以保证数据来源的一致性。生成新的代码以后覆盖以前的文件便可。这样就能保证每一个人生成的代码与当前服务器上的 API 是一一对应的。不过须要注意的是,生成的文件不能手动修改,不然修改最终会被覆盖。

Q: 如何快速得知 API 的变化?

A: 跟 package-lock.json 同样,咱们会对生成的代码进行排序,以减小生成文件的变化。从新运行命令以后,经过 git diff 就能准确得知 API 的变化。

Q: 如何进行多人协做?

A: 若是后端修改了一个字段名,可能会致使前端全部用到这个字段的地方都发生编译错误。这时若是你们都去修改编译问题,不只可能产生冲突,还会形成时间的浪费。虽然这个问题在没有自动化工具以前同样存在,但仍然须要解决。好在这个问题发生的频率不高,咱们能够和项目成员约定:若是有人正好在作这个功能,那么就由他来修改,不然就由指定的人去协调安排。经过自动化工具,能够更快地完成修改,从而减小阻塞别人工做的时间。

Q: 当后端进度落后于前端时,如何保证先有 Swagger 定义?

A: 因为先后端是两个独立的团队,因此进度也经常不一样。后端可能没法先于前端实现好 API,甚至没法和前端同时开始去作一个功能。而这套方案依赖于 Swagger 定义,必须先有 Swagger 定义才能生成代码。若是前端要先于后端完成某个功能,必须先和后端商定好 API Schema 再进行开发。定义好 API Schema 以后,随之更新 Swagger 定义便可(后端无需实现具体功能)。

最后

自动化工具为先后端搭建了一座桥梁,当后端发生变更时,前端也能及时得知并作出相应修改。不再用担忧后端悄悄改接口了!到目前为止,自动化工具已经为咱们项目生成了上万行代码。不只提高了你们的效率,也减小了由于不遵循契约带来的隐藏 Bug。前端终于不用写大量模板代码了,集成 API 也变成了一件很容易的事情。

若是对代码实现感兴趣,能够移步这里:ts-codegen,或者看看我以前写的这篇文章:基于 React 和 Redux 的 API 集成解决方案

相关文章
相关标签/搜索