文章目录
- 1、Formily表单介绍
- 2、安装依赖
- 2.1、安装内核库
- 2.2、 安装 UI 桥接库
- 2.3、Formily 支持多种 UI 组件生态:
- 3、表单设计器
- 3.1、核心理念
- 3.2、安装
- 3.3、示例源码
- 4、场景案例-登录注册
- 4.1、Markup Schema 案例
- 4.2、JSON Schema 案例
- 4.3、纯 JSX 案例
1、Formily表单介绍
Formily 是一个由阿里开源的动态表单解决方案,主要用于构建和管理复杂的表单界面。支持多种前端框架,包括但不限于 React 和 Vue,支持图形可视化界面设计表单,支持多种 UI 组件集成,Formily 的核心优势在于其灵活性和扩展性,允许开发者以声明式的方式定义表单结构和行为。
同类表单产品比较:
能力 | Ant Design Form | Fusion Form | Formik | React Final Form | React Schema Form | React Hook Form | Formily1.x | Formily2.x |
---|---|---|---|---|---|---|---|---|
自定义组件接入成本 | 4.x接入成本低 | 高 | 低 | 低 | 高 | 高 | 低 | 低 |
性能 | 4.x性能较好 | 差 | 差 | 较好 | 差 | 好 | 非常好 | 非常好 |
是否支持动态渲染 | 否 | 否 | 否 | 否 | 是 | 否 | 是 | 是 |
是否开箱即用 | 是 | 是 | 否 | 否 | 是 | 否 | 是 | 是 |
是否支持跨端 | 否 | 否 | 否 | 否 | 否 | 否 | 是 | 是 |
开发效率 | 一般 | 一般 | 一般 | 一般 | 低 | 一般 | 高 | 高 |
学习成本 | 低 | 低 | 低 | 高 | 高 | 低 | 很高 | |
视图代码可维护性 | 低 | 低 | 低 | 低 | 高 | 低 | 高 | 高 |
场景化封装能力 | 无 | 无 | 无 | 无 | 有 | 无 | 有 | 有 |
是否支持表单预览态 | 否 | 是 | 否 | 否 | 否 | 否 | 是 | 是 |
2、安装依赖
2.1、安装内核库
使用 Formily 必须要用到@formily/core,它负责管理表单的状态,表单校验,联动等等。
npm install --save @formily/core
2.2、 安装 UI 桥接库
单纯有了内核还不够,我们还需要一个 UI 库来接入内核数据,用来实现最终的表单交互效果,对于不同框架的用户,我们有不同的桥接库。
Vue 用户
npm install --save @formily/vue
2.3、Formily 支持多种 UI 组件生态:
包含 @formily/antd、@formily/antd-v5、@formily/antd-mobile、@formily/next、@formily/element、@formily/element-plus、@formily/antdv、@formily/vant 、@formily/semi、@formily/tdesign-react、aliyun teamix、antd-formily-boost。
3、表单设计器
Formily 表单设计器是基于designable而扩展出来的扩展包,它在继承了 designable 的基础能力上,提供了 Formily 基础表单的搭建和配置能力。
3.1、核心理念
Designable 的核心理念是将设计器搭建变成模块化组合,一切可替换,Designable 本身提供了一系列开箱即用的组件给用户使用,但是如果用户对组件不满意,是可以直接替换组件,从而实现最大化灵活定制,也就是 Designable 本身是不会提供任何插槽 Plugin 相关的 API
3.2、安装
Ant Design 用户
npm install --save @designable/formily-antd
Alibaba Fusion 用户
npm install --save @designable/formily-next
3.3、示例源码
import 'antd/dist/antd.less'
import React, { useMemo } from 'react'
import ReactDOM from 'react-dom'
import {Designer, //设计器根组件,主要用于下发上下文DesignerToolsWidget, //画板工具挂件ViewToolsWidget, //视图切换工具挂件Workspace, //工作区组件,核心组件,用于管理工作区内的拖拽行为,树节点数据等等...OutlineTreeWidget, //大纲树组件,它会自动识别当前工作区,展示出工作区内树节点ResourceWidget, //拖拽源挂件HistoryWidget, //历史记录挂件StudioPanel, //主布局面板CompositePanel, //左侧组合布局面板WorkspacePanel, //工作区布局面板ToolbarPanel, //工具栏布局面板ViewportPanel, //视口布局面板ViewPanel, //视图布局面板SettingsPanel, //右侧配置表单布局面板ComponentTreeWidget, //组件树渲染器
} from '@designable/react'
import { SettingsForm } from '@designable/react-settings-form'
import {createDesigner,GlobalRegistry,Shortcut,KeyCode,
} from '@designable/core'
import {LogoWidget,ActionsWidget,PreviewWidget,SchemaEditorWidget,MarkupSchemaWidget,
} from './widgets'
import { saveSchema } from './service'
import {Form,Field,Input,Select,TreeSelect,Cascader,Radio,Checkbox,Slider,Rate,NumberPicker,Transfer,Password,DatePicker,TimePicker,Upload,Switch,Text,Card,ArrayCards,ObjectContainer,ArrayTable,Space,FormTab,FormCollapse,FormLayout,FormGrid,
} from '../src'GlobalRegistry.registerDesignerLocales({'zh-CN': {sources: {Inputs: '输入控件',Layouts: '布局组件',Arrays: '自增组件',Displays: '展示组件',},},'en-US': {sources: {Inputs: 'Inputs',Layouts: 'Layouts',Arrays: 'Arrays',Displays: 'Displays',},},
})const App = () => {const engine = useMemo(() =>createDesigner({shortcuts: [new Shortcut({codes: [[KeyCode.Meta, KeyCode.S],[KeyCode.Control, KeyCode.S],],handler(ctx) {saveSchema(ctx.engine)},}),],rootComponentName: 'Form',}),[])return (<Designer engine={engine}><StudioPanel logo={<LogoWidget />} actions={<ActionsWidget />}><CompositePanel><CompositePanel.Item title="panels.Component" icon="Component"><ResourceWidgettitle="sources.Inputs"sources={[Input,Password,NumberPicker,Rate,Slider,Select,TreeSelect,Cascader,Transfer,Checkbox,Radio,DatePicker,TimePicker,Upload,Switch,ObjectContainer,]}/><ResourceWidgettitle="sources.Layouts"sources={[Card,FormGrid,FormTab,FormLayout,FormCollapse,Space,]}/><ResourceWidgettitle="sources.Arrays"sources={[ArrayCards, ArrayTable]}/><ResourceWidget title="sources.Displays" sources={[Text]} /></CompositePanel.Item><CompositePanel.Item title="panels.OutlinedTree" icon="Outline"><OutlineTreeWidget /></CompositePanel.Item><CompositePanel.Item title="panels.History" icon="History"><HistoryWidget /></CompositePanel.Item></CompositePanel><Workspace id="form"><WorkspacePanel><ToolbarPanel><DesignerToolsWidget /><ViewToolsWidgetuse={['DESIGNABLE', 'JSONTREE', 'MARKUP', 'PREVIEW']}/></ToolbarPanel><ViewportPanel><ViewPanel type="DESIGNABLE">{() => (<ComponentTreeWidgetcomponents={{Form,Field,Input,Select,TreeSelect,Cascader,Radio,Checkbox,Slider,Rate,NumberPicker,Transfer,Password,DatePicker,TimePicker,Upload,Switch,Text,Card,ArrayCards,ArrayTable,Space,FormTab,FormCollapse,FormGrid,FormLayout,ObjectContainer,}}/>)}</ViewPanel><ViewPanel type="JSONTREE" scrollable={false}>{(tree, onChange) => (<SchemaEditorWidget tree={tree} onChange={onChange} />)}</ViewPanel><ViewPanel type="MARKUP" scrollable={false}>{(tree) => <MarkupSchemaWidget tree={tree} />}</ViewPanel><ViewPanel type="PREVIEW">{(tree) => <PreviewWidget tree={tree} />}</ViewPanel></ViewportPanel></WorkspacePanel></Workspace><SettingsPanel title="panels.PropertySettings"><SettingsForm uploadAction="https://www.mocky.io/v2/5cc8019d300000980a055e76" /></SettingsPanel></StudioPanel></Designer>)
}ReactDOM.render(<App />, document.getElementById('root'))
4、场景案例-登录注册
4.1、Markup Schema 案例
import React from 'react'
import { createForm } from '@formily/core'
import { createSchemaField } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import * as ICONS from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})const SchemaField = createSchemaField({components: {FormItem,Input,Password,VerifyCode,},scope: {icon(name) {return React.createElement(ICONS[name])},},
})export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField><SchemaField.Stringname="username"title="用户名"requiredx-decorator="FormItem"x-component="Input"x-validator={{required: true,}}x-component-props={{prefix: "{{icon('UserOutlined')}}",}}/><SchemaField.Stringname="password"title="密码"requiredx-decorator="FormItem"x-component="Password"x-component-props={{prefix: "{{icon('LockOutlined')}}",}}/></SchemaField><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField><SchemaField.Stringname="phone"title="手机号"requiredx-validator="phone"x-decorator="FormItem"x-component="Input"x-component-props={{prefix: "{{icon('PhoneOutlined')}}",}}/><SchemaField.Stringname="verifyCode"title="验证码"requiredx-decorator="FormItem"x-component="VerifyCode"x-component-props={{prefix: "{{icon('LockOutlined')}}",}}x-reactions={[{dependencies: ['.phone#value', '.phone#valid'],fulfill: {state: {'component[1].readyPost': '{{$deps[0] && $deps[1]}}','component[1].phoneNumber': '{{$deps[0]}}',},},},]}/></SchemaField><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}
4.2、JSON Schema 案例
import React from 'react'
import { createForm } from '@formily/core'
import { createSchemaField } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import * as ICONS from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})const SchemaField = createSchemaField({components: {FormItem,Input,Password,VerifyCode,},scope: {icon(name) {return React.createElement(ICONS[name])},},
})const normalSchema = {type: 'object',properties: {username: {type: 'string',title: '用户名',required: true,'x-decorator': 'FormItem','x-component': 'Input','x-component-props': {prefix: "{{icon('UserOutlined')}}",},},password: {type: 'string',title: '密码',required: true,'x-decorator': 'FormItem','x-component': 'Password','x-component-props': {prefix: "{{icon('LockOutlined')}}",},},},
}const phoneSchema = {type: 'object',properties: {phone: {type: 'string',title: '手机号',required: true,'x-validator': 'phone','x-decorator': 'FormItem','x-component': 'Input','x-component-props': {prefix: "{{icon('PhoneOutlined')}}",},},verifyCode: {type: 'string',title: '验证码',required: true,'x-decorator': 'FormItem','x-component': 'VerifyCode','x-component-props': {prefix: "{{icon('LockOutlined')}}",},'x-reactions': [{dependencies: ['.phone#value', '.phone#valid'],fulfill: {state: {'component[1].readyPost': '{{$deps[0] && $deps[1]}}','component[1].phoneNumber': '{{$deps[0]}}',},},},],},},
}export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField schema={normalSchema} /><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField schema={phoneSchema} /><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}
4.3、纯 JSX 案例
import React from 'react'
import { createForm } from '@formily/core'
import { Field } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import { UserOutlined, LockOutlined, PhoneOutlined } from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><Fieldname="username"title="用户名"requireddecorator={[FormItem]}component={[Input,{prefix: <UserOutlined />,},]}/><Fieldname="password"title="密码"requireddecorator={[FormItem]}component={[Password,{prefix: <LockOutlined />,},]}/><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><Fieldname="phone"title="手机号"requiredvalidator="phone"decorator={[FormItem]}component={[Input,{prefix: <PhoneOutlined />,},]}/><Fieldname="verifyCode"title="验证码"requiredreactions={(field) => {const phone = field.query('.phone')field.setComponentProps({readyPost: phone.get('valid') && phone.get('value'),phoneNumber: phone.get('value'),})}}decorator={[FormItem]}component={[VerifyCode,{prefix: <LockOutlined />,},]}/><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}
没有谁能击垮你,除非你自甘堕落。