前言
初识formily
是在好几年前,还为它有写过一篇文章:uform使用笔记。但遗憾的是,一直没有将它用于实际的业务。
直至去年的2月份,因缘巧合下,在业务项目中开始使用v1.x版本。好像那会儿formily
作者在推v2.x的beta版本,但了解到基本上是断崖式升级
,就放弃了,毕竟那会儿用1.x已经开发了很多代码了,是真的不敢动。当然,其实用微前端的话,也可以用formily
2.x的beta版本,只是那会儿还没有必要用到微前端
。
好巧不巧,我所在的项目组在去年9月因为一些原因,被迫解散,然后去了新的团队,刚好又有了尝试formily
的机会,所以果断用了beta
的版本,期间也经历过一些不太稳定吧,好在后面正式升了v2的版本,好了很多。
新手期
在刚开始用的时候,也是各种查文档,向身边用过formily
的同事请教写法。慢慢地,明白了其实也就两种写法:
- json schema
- 纯JSX
其实文档中还有第三种写法,叫markup schema
,但个人觉得这个蛮鸡肋的,比如说中间想写一个div
的结构,它会将这个div
插入到最后(明显不满足自己的需求)
我们的业务场景是物流Sass
,在详情页面会有N
个表单项
。因为我个人比较倾向于写jsx
,所以在刚开始的时候,就各种写JSX。但写着写着,发现好累啊,而且有些代码长着也差不多,于是想着再封装一层,是json,但是类型是这样的:
export interface ISchema {
component: 'Row';
componentProps?: RowProps;
areaList: {
key: string;
colSpan?: string;
componentProps?: any; // 可以是col的props或者div的props
useGrid?: boolean;
formItemProps?: IFormItemProps;
gridProps?: {
minWidth?: number | number[];
maxWidth?: number | number[];
minColumns?: number | number[];
maxColumns?: number | number[];
breakpoints?: number[];
columnGap?: number;
rowGap?: number;
colWrap?: boolean;
};
children: {
name?: string;
title?: string;
key: string;
type: 'basic' | 'custom';
component: string;
dataSource?: {
label: string;
value: any;
}[];
rules?: Rule[];
componentProps?: {
[key: string]: any;
};
fieldProps?: {
[key: string]: any;
};
formItemProps?: IFormItemProps;
gridColumnProps?: {
gridSpan?: number;
};
}[];
}[];
}
简单说一下,为什么要那么设计。。因为我们的详情页面布局,差不多可以分成一个或者多个区块,每个区块里面就是grid
布局。
然后拿它做了一些实验,确实跑的很好。
成长期
这个json写多了,也会有一些思考,就是感觉和formily
本身提供的json schema
格格不入。
直至后面领导让我考虑,通过低代码来配置出来这些json。主要是有几个方面的考虑:
- 权限(按钮/表单项)
- 校验规则(前后端尽量使用同一套)
- 国际化
讲真,开始挠头了。然后认真去读了一下formily
提供的json schema协议后,发现其实它设计的挺好的,并且有现成的designer设计器
。
在改回json schema
的过程中,我重新审视了之前的ISchema
的设计,发现我自己写的自定义组件,完全没有考虑type: void
的情况,就导致了有些地方会去重置basePath
,其实这是反模式的。
磐石设计
磐石是这个低代码的一个代号。下面我简单分享一下。
前置内容
这个低代码平台的产物是json schema
.
业务工程里面,根据页面
来获取当前页
下面所有的json list
(一个页面有些地方是割裂的,所以会拆成多个json,包括还可能存在弹窗里面的表单)。
对应块只要渲染匹配名称
的json schema
即可。
页面部分
新增弹窗
需要说明的是为什么会有按钮
。因为在业务工程的保存按钮点击后,后面要根据请求URL
找到它对应的按钮
,按钮里面会有两个东西:
- 权限(即这个用户有没有权限点这个保存)
- 去查找它对应的所有的json列表(从而拿到所有表单项的
rule list
)
tips
: 按钮也会有一个专门的增删改查,这里面有一个要考虑的点是权限,譬如说权限code间的组件,譬如:
{{a}} && {{b} || {{c}
a/b/c
是代表权限code,这里为什么要用插值表达式
呢?因为到前端的时候,需要replace
成true
or false
磐石列表
功能简单说明:
- 编辑:弹窗里面的内容
- JSON编辑:formily的desiger
- 删除
- 复制:主要是复制JSON编辑器生成出来的
json schema
设计器详情
更深入的内容
权限
有两个地方的权限:
- 按钮
- 表单项
按钮的话,上面已经讲过了,无非就是给它一个权限表达式。前端处理一下即可。
表单的处理方式,就略巧妙了,咱们可以扩展一个x-permission
。然后解析schema
的时候处理一下即可。
Schema.registerPatches(schema => {
// sole.log(schema['x-permission']);
const permission = schema['x-permission'];
if (permission) { // 这里做一些处理
schema['x-display'] = 'none';
}
return schema;
});
校验规则
<Button
onClick={async () => {
const json = transformToSchema(designer.getCurrentTree());
await saveDetail({
json: json.schema,
processJson: filterVoidSchema(json.schema as ISchema),
relaId: id,
name,
});
message.success('保存成功');
}}
>
<TextWidget>Save</TextWidget>
</Button>
看上面的代码,在进行保存的时候,其实是往后端传了两个json
的,一个是前端需要的json,一个是后端需要的json。
我们看函数名:fiterVoidSchema
,过滤掉void的schema,因为前端布局会造成很多冗余的json项,让后端去做递归也可以,但前端更方便一些吧。
后端可以快速地拿到x-rules
的字段,从而做到前后端的校验统一
国际化的处理
不知道大家有没有用过kiwi。我司基于它改造了一番,因为它之前内置google翻译,然后我们改成了百度翻译,且另外加了自己的一些扩展。
话说回来,这是一个比较好的方案,但不是一个最优的方案。因为文案的增多,会造成国际化文件的不断庞大,譬如下面的两段文案:
你确定删除么?
你确定要删除?
其实它俩是一个意思。我们这个平台的国际化方案如下:
- label的国际化,是通过后端去处理我上传的
json schema
- 提示类的,统一写一个方法,传入还是中文,传出的值则取决于当前的语言环境。当然页面会加载一个文件(中文为key,语言环境为value的json)
- 页面里面本身的中文(这一块暂时没想法,也许还是会采用
kiwi
)
未来要做的事
其实要做的事还有很多,譬如说引用磐石页面(类似公共池的概念,公共的地方componentProps改了,引用处也要修改掉)。
说实话,很挠头。。。愁得我头发都快掉光了,有类似需求、想法的朋友们,可以一起交流一下。。
写在最后
关于formily
如何使用,其实在文中,我基本没怎么提到,大家可以自行参考文档。说实话,成本确实有,但是学过一次,受益终生(这是不可能的,但好歹能让你吃点红利)。
对比可视化引擎工具
,formily designer
的扩展性还有待提高(这里是指我们往里面加一个组件,需要在很多地方操作),但各有各的优势。