前言

Formily是阿里开源的动态化表单的解决方案,优雅的解决了多种复杂场景的表单的数据、状态管理及夸表单通信问题,同时规避了全量渲染的弊端,性能优越。刚好满足我近期工作中的业务需求,啃了将近一周的文档,对Family有了初步的了解,后续使用起来有时间再出翻坑指南。

初步了解

Formily基于MVVM的设计原则,常用的基础核心库有@formily/core@formily/react@formily/vue@formily/antd,支持react和vue,同时支持Markup Schema、 JSX以及现在业界最流行的JSON Schema的写法。

Formily 分为了内核层UI 桥接层扩展组件层,和配置应用层,如下图。
内核层是 UI 无关的,它保证了用户管理的逻辑和状态是不耦合任何一个框架。
JSON Schema 独立存在,给 UI 桥接层消费,保证了协议驱动在不同 UI 框架下的绝对一致性,不需要重复实现协议解析逻辑。
扩展组件层,提供一系列表单场景化组件,保证用户开箱即用。无需花大量时间做二次开发。
image.png

核心优势

  • 高性能
  • 开箱即用
  • 联动逻辑实现高效
  • 跨端能力,逻辑可跨框架,跨终端复用
  • 动态渲染能力

核心劣势:学习成本较高,需引多个包,包体积较大。

确实Formily的学习成本还是相对较高的,几大核心库一堆的文档和API会让你眼花缭乱,但是既然要学就要耐下心来,思路理清之后就轻车熟路了。

核心库介绍:
@formily/coreViewModel层,负责管理表单的状态,表单校验,联动等等。
@formily/react Model层,作为UI 库来接入内核数据,用来实现最终的表单交互效果,对于不同框架的用户,使用有不同的桥接库,这里使用react为例,使用vue安装@formily/vue
@formily/antdView层,在Ant Design基础之上封装的开箱即用的组件库。

使用准备

安装依赖,这里以React+Antd为例

使用yarn
yarn add @formily/core
yarn add @formily/react
yarn add antd moment @formily/antd

or使用npm
npm install --save @formily/core
npm install --save @formily/react
npm install --save antd moment @formily/antd

导入依赖:

  1. import React from 'react'
  2. import { createForm } from '@formily/core'
  3. import { FormProvider, Field } from '@formily/react'
  4. import { FormItem, Input } from '@formily/antd'

实例

JSON Schema写法为例:

  1. import React from 'react';
  2. import { createForm } from '@formily/core';
  3. import { createSchemaField, FormConsumer, FormProvider } from '@formily/react';
  4. import { FormItem, Input, Password, Submit, FormLayout, FormButtonGroup } from '@formily/antd';
  5. import * as ICONS from '@ant-design/icons';
  6. //用来创建表单核心领域模型,它是作为MVVM设计模式的标准 ViewModel
  7. const form = createForm();
  8. // 创建一个 SchemaField 组件用于解析JSON-Schema动态渲染表单的组件
  9. const SchemaField = createSchemaField({
  10. // 组件列表
  11. components: {
  12. FormLayout,
  13. FormItem,
  14. Input,
  15. Password,
  16. },
  17. // 全局作用域,用于实现协议表达式变量注入
  18. scope: {
  19. icon(name: string) {
  20. return React.createElement(ICONS[name]);
  21. },
  22. },
  23. });
  24. /**初始化一份json schema
  25. * 解析 json-schema 的能力;将 json-schema 转换成 Field Model 的能力;编译 json-schema 表达式的能力
  26. **/
  27. const schema = {
  28. // schema type
  29. type: 'object',
  30. properties: {
  31. layout: {
  32. type: 'void',
  33. 'x-component': 'FormLayout',
  34. 'x-component-props': {
  35. labelCol: 2,
  36. wrapperCol: 6,
  37. labelAlign: 'right',
  38. // layout: 'vertical',
  39. },
  40. // 属性描述
  41. properties: {
  42. username: {
  43. // schema type
  44. type: 'string',
  45. // 标题
  46. title: '用户名',
  47. // 必填
  48. required: true,
  49. // 字段 UI 包装器组件
  50. 'x-decorator': 'FormItem',
  51. // 字段 UI 组件
  52. 'x-component': 'Input',
  53. // 字段 UI 组件属性
  54. 'x-component-props': {
  55. prefix: "{{icon('UserOutlined')}}",
  56. },
  57. },
  58. password: {
  59. type: 'string',
  60. title: '密码',
  61. required: true,
  62. 'x-decorator': 'FormItem',
  63. 'x-decorator-props': {
  64. addonAfter: '强度高',
  65. },
  66. 'x-component': 'Password',
  67. 'x-component-props': {
  68. prefix: "{{icon('LockOutlined')}}",
  69. },
  70. },
  71. },
  72. },
  73. },
  74. };
  75. export default () => {
  76. return (
  77. <FormProvider form={form}>
  78. <FormLayout layout="vertical">
  79. <SchemaField schema={schema} />
  80. </FormLayout>
  81. <FormButtonGroup>
  82. <Submit onSubmit={console.log}>提交</Submit>
  83. </FormButtonGroup>
  84. <FormConsumer>
  85. {() => (
  86. <div
  87. style={{
  88. width: 340,
  89. marginTop: 20,
  90. padding: 5,
  91. border: '1px dashed #666',
  92. }}
  93. >
  94. 实时响应-用户名:{form.values.username}
  95. </div>
  96. )}
  97. </FormConsumer>
  98. </FormProvider>
  99. );
  100. };

1638349667(1).png

  • FormProvider组件是作为视图层桥接表单模型的入口,它只有一个参数,就是接收 createForm创建出来的 Form实例,并将 Form实例以上下文形式传递到子组件中。
  • FormLayout组件是用来批量控制FormItem样式的组件,这里我们指定布局为水平布局,也就是标签在左,组件在右。
  • FormConsumer组件是作为响应式模型的响应器而存在,它核心是一个render props模式,在作为 children的回调函数中,会自动收集所有依赖,如果依赖发生变化,则会重新渲染,借助 FormConsumer我们可以很方便的实现各种计算汇总的需求。
  • FormButtonGroup组件作为表单按钮组容器而存在,主要负责按钮的布局。
  • Submit组件作为表单提交的动作触发器而存在,其实我们也可以直接使用 form.submit方法进行提交,但是使用 Submit的好处是不需要每次都在 Button组件上写 onClick事件处理器,同时它还处理了 Formloading状态,如果 onSubmit方法返回一个 Promise,且 Promise正在 pending状态,那么按钮会自动进入 loading状态。


注意:使用前还需要了解,Formily已经完全放弃对IE的兼容,如需兼容IE,慎用!!!

参考

Formily官方文档