Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

聊聊接口文档生成页面代码 #27

Open
winixt opened this issue Nov 29, 2022 · 0 comments
Open

聊聊接口文档生成页面代码 #27

winixt opened this issue Nov 29, 2022 · 0 comments

Comments

@winixt
Copy link
Owner

winixt commented Nov 29, 2022

问题

B 端系统有很多“相似”的样板代码(胶水代码),但是很多细节又不一样,例如一个常规的列表页面:

  • 有的列表页有翻页,有的没有
  • 有的列表页有搜索按钮,有的没有
  • 每个列表页的搜索条件大概率也不一样,输入框、下拉框、日期组件的各种组合
  • 有的列表页有搜索,有的没有
  • 有的列表页详情,有的没有
    ....

其他详情页、表单页面都有类似的情况。每天写大量这种无聊的代码,简直是浪费生命。

问题分析

这种内部细节差异非常多的场景,不好对其进行抽象,每种场景的问题域还不一样,列表页有列表页的难题,表单页有表单页的难题。强行沉淀为业务组件,会面临配置多、不方便后续扩展等问题。相对比,较好的方向是,能通过某种手段直接生成类似手写的代码,这样既避免写大量胶水代码的苦力活,又能让开发用他们之前熟悉的开发模式,在生成代码基础上继续扩展高级功能。

代码不能凭空出现,页面的关键信息点不能少,比如有哪些字段、字段类型、字段中文名、字段间的关系、页面交互信息等等,可以统称为:描述完整页面的最小信息集。这个“最小信息集”从哪来?最简单的方案是给个配置平台,让用户手动录入,再生成页面。但是现代 IDE 环境越来越智能,代码智能提示,文本替换,相比用户手动录入信息,不见得直接写胶水代码体验会更差好,或者更慢。如果一个团队的研发流程比较成熟,肯定会有这两个文档:

  • 需求文档
  • 接口文档

通过这两个文档,能直接分析出很多“最小信息集”里面的信息,从而需要用户录入的信息就很少了,在部分常规的场景下,甚至不需要再录入。导入接口文档,即直接生成页面。类似于下面这个 demo:

image

设计

要实现上述系统,可以采用如下架构,以提升系统的灵活性和可扩展性。

image

  • Compiler 模块,只负责根据 Page Schema 编译生成代码。
  • Adaptor 模块,约定 API Schema 的格式,可根据不同 UI 库实现不同 Adaptor 生成 Page Schema
  • Client 模块,客户端页面交互,解析接口文档和需求文档,生成 API Schema
  • Server 模块,配置管理,获取接口文档等,如果接口文档有多套,可将接口文档的识别解析,从 Client 移到 Server 模块。

Compiler 模块

Compiler 生成的代码分为两部分:可复用的公共代码和不可复用的组件代码。在生成公共代码之前,Compiler 会先扫描原项目是否已经生成过了,假设现在 Page Schema 声明依赖了 utils.js 文件下的 pick 函数,Compiler 会先检测 utils.js 是否含有 pick 函数,如果有则跳过,没有则注入一个新的 pick 函数。对于不可复用的组件代码(.vue 文件),Compliler 每次都会生成新的进行覆盖。因此在对不可复用代码进行二次修改后不可再生成,否则会直接覆盖。关键数据类型定义如下:

Page Schema 的类型定义:

字段 字段描述 类型
componentsTree 页面组件树 ContainerComponent[]
css 页面 css CSS
jsCodes 页面依赖的外部 js 代码 JSCode[]
dependencies 页面依赖的外部 npm 库 DependentResource[]

容器组件 ContainerComponent 可以是一个页面,或者一个组件。例如一个单文件组件 SFCComponent:

字段 字段描述 类型
componentName 容器组件名称,固定为 SFCComponent -
dir 组件输出目录 string
filename 组件输出文件名 string
children 子组件 Component[]
setupCodes vue setup 的脚本代码 SetupCode[]
css 组件 css 代码 CSS
propsDefinition 组件 props 定义 ComponentPropDefinition[]
emitsDefinition 组件事件定义 string[]

普通组件 Component 的类型定义,

CSS 类型定义,如果输出的文件的是个文件,则需要对已有 css 文件进行 AST 语法解析,增量修改 css 内容,不能直接覆盖,方便对代码的二次修改。

字段 字段描述 类型
lang css 类型 cssless
content css 内容 string
dir 输出目录,可选 string
filename 输出文件名,可选 string
scoped css 域 boolean

JSCode 类型定义,描述依赖的 js 代码,如果输出的文件的是个文件,则需要对已有 js 文件进行 AST 语法解析,按需修改 js 文件代码,不能直接覆盖,方便对代码的二次修改。

字段 字段描述 类型
content js 代码 string
dir 输出目录 string
filename 输出文件名 string

DependentResource 类型定义,描述页面的外部依赖,修改 package.json 文件

字段 字段描述 类型
package 包名 string
version 包版本 string

Adaptor 模块

Adaptor 接收的输入为 API Schema,结合适配器里面的 UI 信心和部分胶水代码,生成 Page Schema。举个生成表单的例子:
image

API Schema 结构如下:

字段 字段描述 类型
url 接口地址 string
method 请求方法 string
headers 请求头 object
params 请求请求参数 Field[]
resData 响应数据 Field[]
pagination 分页数据 Field[]

Field 字段类型定义如下:

字段 字段描述 类型
name 字段名 string
alias 字段别名,用于数据转义 string
title 字段中文名 string
type 字段类型 string
checked 是否生效 boolean
mappingId 数据 Map string
options 可选 Option Option[]
mapFields 对应后端真实字段名,例如一个时间范围选择器,在前端是一个数组,对后端接口是两个字段 xxxStartTime, xxxEndTime string[]
component 指定渲染组件 RenderComponent
apiSchema 字段相关的其他接口 API Schema

Client 模块

Client 模块主要包含三块功能:

分析用户选择的页面“主接口”,推导生成初步“最小信息集合”

除了静态页面,其他B端系统至少有一个“主接口”,例如表单页面的新增/修改接口,列表页面的列表查询接口。用列表页举例:

当用户选了一个列表查询接口之后,大概步骤包含(根据场景可以丰富更多的策略,以提升识别能力):

  1. 分析接口的数据结构,请求方法、请求参数、响应体结构
  2. 分析接口的字段信息,包括字段类型、字段枚举、字段名字
  3. 丰富字段信息,根据字段渲染位置(宣称成 form or 渲染成 table)填充渲染组件、组件参数、组件附加信息等。
  4. 根据接口路径和接口的字段的信息,可以分析出与这个页面相关的其他接口,比如新增接口、删除接口、修改接口、导出接口、枚举查询接口等等。

解析需求文档,完善“最小信息集”

接口文档有时候不够详细,字段中文缺失,或者描述错误,字段格式缺失。还有些信息是接口文档不具备的,例如字段的一些校验信息,枚举值的中文信息。这些可以从需求文档中提取,填充到“最小信息集”,以减少用户的配置成本。

对 “最小信息集” 的增/删/改

这个就是一些前端页面的基本配置了,提供一个配置界面即可。

Server 模块

由于我是个前端开发,大部分前端能做的,自然而然就放到前端去了,给服务端这块分配的功能相对弱很多,包含配置管理和接口文档请求就差不多了,按实际功能划分,接口分析放到服务端感觉合适一点,这样前端就纯粹一些。

最后附上一个简单的实现传送门

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant