【中后台应用】从表单抽象到表单中台
相信前端开发的同学,对表单其实并不陌生,而且时至今日,表单应用的编写因为React、Vue等框架的出现,也变得更加地便捷了。在前端工作中,有着很多中后台应用-表单的开发工作量,笔者自己深陷其中,所以为了让头发别掉得太快,重新去理解了表单这个东西,从而重新去思考和设计表单的开发模式,提升效率。
理解表单
其实大家都知道表单是什么,但大多数人对它应该没有一个明确地认识,至少我之前是没有的。
基础表单
<!-- https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Forms/Your_first_HTML_form -->
<form action="/my-handling-form-page" method="post">
<div>
<label for="name">Name:</label>
<input type="text" id="name" />
</div>
<div>
<label for="mail">E-mail:</label>
<input type="email" id="mail" />
</div>
<div>
<label for="msg">Message:</label>
<textarea id="msg"></textarea>
</div>
<div class="button">
<button type="submit">Send your message</button>
</div>
</form>
复制代码
这段代码完成了一个最为基础的表单,我们来分析下,它都有什么?
- 提交地址、提交方法
- 提示信息
- 输入框
- 提交按钮 然后今时今日,这样简单的表单其实并不再能满足越发复杂的应用需求了。
更丰富的表单
在有了JQ、React、Vue等等之后,网络和社区上有了更为丰富的表单组件,比如日期选择、时间选择器、图片裁剪上传等等。
//https://codepen.io/pen/?&editable=true&editors=001
const { TimePicker } = antd;
function onChange(time, timeString) {
console.log(time, timeString);
}
ReactDOM.render(
<TimePicker onChange={onChange} defaultOpenValue={moment('00:00:00', 'HH:mm:ss')} />,
mountNode
);
复制代码
定义表单
可不管怎么变化,提交地址和提交方法、提示信息、用户输入、提交按钮,这些都是不可或缺的,于是我尝试用简练的语言来抽象一下表单是个什么东西:
表单是一个将人机交互行为转换为数据后提交给服务器的可视化前端应用。
想要理解表单,这两个词就尤为关键:
- 人机交互行为
- 转换为数据
人机交互行为
为什么不是表单组件(输入框、上传文件、选择框等等),而是人机交互行为?因为在表单应用的开发中,会有更多地用户对数据进行输入场景,而基本的表单组件只是其中的一类行为而已,如果哪天我们的表单里面,需要用户在一个画板上画圈圈呢?
转换为数据
我们最终与服务器进行传递的东西,不过是一份数据而已,而表单很重要的一个作用,就是将人机交互的行为转换为计算机能够存储的数据,然后与接收方进行通信。
所以,表单是这样的:
高级模式-动态表单
聪明的开发者当然不想每天都重复地写着类似的代码,动态表单也是因此而生的吧。
动态表单,说白了就是只需要通过一份配置,就能生产一个表单应用。它能够极大地提升我们的效率,组件的复用率等等。开发者从写代码到了写配置。
就算没有对表单进行明确的理解,动态表单的组件、框架或者库类,其实都已经存在着很长的一段时间了。但它却还是存在着一些问题:
- 有学习配置的成本
- 有开发和维护配置的成本
为了解决它的问题,于是笔者基于动态表单,设计了一个表单中台。
表单中台的设计
表单中台是通过对表单进行了抽象,然后单独针对网站应用上的所有表单而设计的。
它是对一个网站应用上面的所有表单,从前端开发者对表单相关的开发维护到用户提交数据到服务端,这样一个完整链路的抽象封装。
表单配置的可视化生产
表单的配置化其实就是将表单开发的逻辑,转化成为了一种结构,在前端看来,它是个JSON或者是个对象。手动编写表单配置是可以被可视化的工具所替代的,这样,表单的开发和维护就变得更加清晰、简便了,效率也会得到提升。
一份配置对应着一个表单的时候,但我们在一个网站应用(业务)上有多种场景需要多个表单,这时候就会有多份配置,多份配置会就需要对齐进行管理,甚至需要动态化异步加载配置。我把配置相关的事情,也一并列入表单中台的设计之中,让链路更加地完整。
说到这里,有些人可能会联想到一些问卷调查的网站、应用。本质上,它们是一样的,但问卷调查应用与大家复杂地表单开发工作还是会有很大的不一样,所以当有表单需求来了的时候,你不会告诉你的业务方说,"你去建立个问卷调查就好啦”。而它与问卷调查系统不一样的地方就是一个商业系统与中台系统的区别。所谓的中台,就是用来驱动和支撑商业系统的。
而实现可视化的手段,就是通过表单来生产配置,然后渲染表单。
可视化生产表单配置的页面:
表单中台
表单中台是一个可以完全由前端驱动的产品,因为表单里面跟数据存储查询是可以相对对立的部分,不管数据跟哪个服务器进行通信,都是不需要关心的,标准应该有前端进行制定。这样,它就是一个去中心化的产品,同时也具备成为一个中台的可能。因为它是一个中台,所以它也是能够支撑和驱动各种N个中后台和业务发展的。
架构设计
通信层与服务器
通信层磨平了与服务器进行通信的过程,这其中包括了配置的增删改查,表单数据的读写。接口标准由前端进行了定义。
例如配置查询的接口:
参数 | 类型 | 是否必须 | 说明 |
---|---|---|---|
formId | Long | 否 | 表单ID |
type | Long | 是 | 0表示根据userId获取用户的配置列表,1表示根据formId获取某个具体配置 |
核心层
通过之前对表单的抽象,就可以抽象出一些表单相关的JS类,这个些类本质上其实都是一些数据相关的操作,包括但不限于:
- 表单数据验证
- 表单数据读写
- 表单元素的基本属性
/**
* 定义一些标准的表单属性
* - defaultValue
* - key
* - dataSource
* - disabled
* - size
* - title/labelText
*
* 方法:
* - onChange
* - setValue
* - verify
* @param {Object} config
*/
initFormProps(config){
let onChange = (value) => {
this.setValue(value)
this.formChange(this.key,value)
}
let getValue = () => {
return this._value
}
return {
onChange,
getValue,
size: this.config.size,
dataSource: this.dataSource || [],
disabled: this.disabled,
defaultValue: this.defaultValue,
value: this._value,
key: this.key,
block: this.config.block === "true" || this.config.block === "true",
title: this.title || this.labelText,
labelText: this.labelText || this.title,
_type: this.type
}
}
复制代码
有了这些,就可以在渲染层,进行多框架的渲染对接了。
业务层
在输出的时候,同时输出了渲染组件和配置生产组件,配置的生产组件可以不进行上线,只要对接业务接口即可;渲染组件自动拉取对应场景的表单配置进行渲染。
所以,只要一次的接入,后续的表单开发工作,就是三步:
1.(未有满足的表单组件时)开发自定义的表单组件 2. 在配置生产组件创建表单 3. 在对应场景接入渲染组件
总结
开发者的开发历程通常会有四个阶段:写代码、写配置、可视化生产、中台。特别是在中后台的应用场景中,这样路径似乎都是有效的。
本文只是说到了表单的中台,其实,这个思路是可以被复制的,
从表单页面的可视化,到表格页面的可视化,再到中后台站点的可视化,路径与架构设计都会大致相同。因此,为了解放生产力,还有很长的路要走。