配置化的开发方式是什么样的(一)
chenos opened this issue · comments
过去的开发模式
以登录模块为例,登录可能的几种场景:
- 用户名、密码登录
- 手机+验证码登录
- 第三方登录
通常情况,需要做这么些事情:
- 建一张用户数据表,如果是复杂情况,可能还会有相关的关系表
- 后端实现基本的 MVC 之后,提供相关 API 接口给客户端调用
- 客户端实现登录相关 UI 界面
- 前后端调试,这个过程可能需要前后端一起配合
可能存在的一些细节
- 前后端都需要对表单字段进行校验
- 可能需要实现各种客户端的 UI
- 实现相关功能模块的单元测试
- 实现用户注册、用户找回密码等流程
为了完成这样一个模块,我们可能需要找一个全栈,或者一个后端+N个前端来完成相关工作,那怎么可以简化流程或者提升效率呢?
配置化的开发方式是什么样的?
回到刚才提到的登录模块,从用户视角,这个页面可能存在的一些元素:
- 登录页面
- LOGO
- 表单字段
- 用户名字段
- 密码字段
- 其他字段
- copyright 文本
转换成机器可阅读的 json 方式:
{
"type": "page",
"title": "登录页面",
"body": [
{
"type": "image",
"url": "http://imgurl"
},
{
"type": "form",
"apiAction": {
"method": "post",
"url": "/login",
},
"controls": [
{
"type": "text",
"name": "username",
"required": true
},
{
"type": "password",
"name": "username",
"required": true
}
]
},
"copyright 文本"
],
}
转换的目的是为了让机器可以读懂,这份 json 配置就可以在各端共享
- 客户端通过配置把相对应的组件输出到界面
- 服务端通过配置实现表单字段的验证
服务端的配置
刚上面的 json 配置是从客户端用户角度阐述的,完善一下服务端部分吧。尝试把 Routing 也放到配置里,简单改造之后:
{
"type": "form",
"apiAction": {
"method": "post",
"uri": "/login",
// 加了一行 action 来适配路由
"action": "AuthController@login",
},
"controls": [
{
"type": "text",
"name": "username",
"required": true
},
{
"type": "password",
"name": "password",
"required": true
}
]
}
登录的情况,直接映射 action 方法是比较合适的,实际情况比较复杂,所以有必要,重点介绍一下 apiAction
的各种用法。
比如我想直接通过 sql 输出某些字段的数据(实际场景直接 Sql 是有风险的操作,可以把 Query Builder 结合进来)
{
"type": "table",
"apiAction": {
"type": "sql",
"sql": "select id, username from users limit 20",
},
"columns": [
{
"name": "id",
"label": "ID",
},
{
"name": "username",
"label": "用户名",
}
]
}
Graphql 场景。想象空间巨大,非常适合查询 api 的描述,而且跨平台。如获取 id=4 的用户的名字。
{
"apiAction": {
"type": "graphql",
"graphql": "
{
user(id: 4) {
name
}
}
",
}
}
标准的 rest 情况
{
"apiAction": {
"rest": true,
// 指定 model 或者 table
"model": "App\\User",
"table": "users",
"method": "get",
"uri": "/users"
}
}
查询数据,输出表格,字段直接由 columns 提供
{
"type": "table",
"apiAction": {
"rest": true,
"method": "get",
"uri": "/users"
},
"columns": [
{
"name": "id",
"label": "ID",
},
{
"name": "username",
"label": "用户名",
}
]
}
表单插入数据
{
"type": "form",
"apiAction": {
"rest": true,
"method": "post",
"uri": "/users"
},
"controls": [ // 字段直接由 controls 提供
{
"type": "password",
"name": "username",
"required": true
},
{
"type": "password",
"name": "password",
"required": true
}
]
}
表单修改数据
{
"apiAction": {
"rest": true,
"method": "put",
"uri": "/users/{id}"
},
"controls": [ // 字段直接由 controls 提供
{
"type": "text",
"name": "username",
"required": true
},
{
"type": "password",
"name": "password",
"required": true
}
]
}
点击按钮,删除数据
{
"type": "button",
"label": "删除",
"confirmText": "确定删除这条数据吗?",
"apiAction": {
"rest": true,
"method": "delete",
"uri": "/users/{id}"
}
}
apiAction
的就说这些了,其他情况任由你想象了。
如何前后端共享配置
由服务端配置各个页面的 json schema,提取 schema 的 apiAction,生成对应的 router 供客户端 api 调用。
在前端的表现:用户打开某个 url,客户端把 url 转发给服务器,服务器识别 url 之后,输出对应的 json 配置,响应给客户端,客户端通过 json 配置渲染界面。这种方式也带来了一点好处,服务端可以控制客户端 router 了(有很大想象空间),权限的控制比在客户端处理便捷了许多。
配置化的好处
一次编写,多处使用。配置只是用来描述页面、资源或者行为等,具体的语言并没有限制,配置也可以理解为是一种协议,为各种跨平台提供便捷。比如有各种客户端需求,iOS、Android、各平台小程序等,服务端也可能不只是用 PHP 来实现。
配置化在前端的意义
回到最上面的登录页面,以 React 为例,代码如下:
import Renderer from './Renderer';
<Renderer schema={{
"type": "page",
"title": "登录页面",
"body": [
{
"type": "image",
"url": "http://imgurl"
},
{
"type": "form",
"apiAction": {
"method": "post",
"url": "/login",
},
"controls": [
{
"type": "text",
"name": "username",
"required": true
},
{
"type": "password",
"name": "password",
"required": true
}
]
},
"copyright 文本"
],
}}/>
还可以用一种更 Markup 的风格来书写:
import { Page, Image, Form } from 'Renderer';
<Page title={'title'}>
<Image url={'http://imgurl'}/>
<Form apiAction={{
"method": "post",
"url": "/login",
}}>
<Form.Text name={'username'} required/>
<Form.Password name={'username'} required/>
</Form>
copyright 文本
</Page>
Page, Image, Form 并不是真实的组件,而是一种 Markup,用来描述配置/节点而已,真实的组件可以根据实际使用的 ui 组件注入,如:
registerRenderer('page', () => ...);
牵扯的细节很多,就不扩展了,这种书写风格带来的意义是,UI 组件抽离出来了,可以自由的注册各种组件进来,比如 web 端用 web 端的组件,小程序端用小程序的组件,React Native 也可以自由扩展。组件库可以不断壮大,这个组件库是可持续发展的,一次编写,多处使用。
组件的维护是集中、可持续的,第一个人编写,剩余的人只要拿来用就行了,只要大家都遵循一种标准即可,只要遵循标准的变化都可以采纳。维护这样的开发秩序,第一个项目我们可能需要从零开始,10 个项目之后,我们就是站在巨人的肩膀上。
回到最上面,如果我们只需要编写三四个配置文件,就实现了完整的用户登录、登录之后查看用户列表,以及用户的增删改查,而且这个模块多客户端通用,WEB 端、小程序端、APP 端都可以使用。
我们还需要一后端+N前端吗?
可能很多人就要因此下岗了。
开头,先说明一点,我相信有一类人会有这样一个想法:我就一程序员,你让我写晦涩的配置,情何以堪。
我承认配置在某种程度可能会有些封装过度或者矫枉过正,但是恰到好处的配置,绝对是开发利器,以上思路部分细节讲的不深入,大家理解的也可能有偏差,欢迎讨论。