前端开发者对表单场景一定不陌生——从简单的登录注册到复杂的业务系统,表单无处不在。但真正做过企业级复杂表单的同行都清楚,这里面的水有多深。字段联动、动态校验、异步加载、多步骤提交...随便一个需求都能让代码复杂度指数级上升。
传统表单方案通常面临三大困境:
Formily 2.3.0的发布,正是针对这些痛点给出了系统性的解决方案。作为阿里开源的面向中后台复杂场景的表单解决方案,新版本在以下方面实现了突破:
Formily 2.3.0采用了清晰的三层架构:
code复制[视图层] ←→ [协议层] ←→ [模型层]
模型层是整个表单的核心大脑,负责:
协议层定义了表单的描述规范,主要包括:
视图层则与具体UI框架解耦,目前支持:
这种分层设计带来的最大优势是:业务逻辑与UI实现彻底解耦。开发者可以先用JSON描述表单结构,再根据需要选择不同的UI渲染方案。
字段联动是复杂表单最头疼的问题之一。Formily 2.3.0引入了基于Proxy的响应式系统,使得字段间的依赖关系可以声明式定义:
javascript复制{
"x-reactions": [
{
"dependencies": ["sourceField"],
"when": "{{ $deps[0] === 'show' }}",
"fulfill": {
"state": {
"visible": true
}
}
}
]
}
这套机制实现了:
新版本重构了校验规则系统,主要改进包括:
javascript复制{ validator: '(required && minLength:5) || pattern:/^\\d+$/' }
javascript复制{ validator: async (value) => await checkAPI(value) }
javascript复制{ validator: (value) => value === form.values.confirmPassword }
首先安装依赖:
bash复制npm install @formily/core @formily/react @formily/antd --save
基本表单结构:
jsx复制import { createForm } from '@formily/core'
import { FormProvider, Field } from '@formily/react'
import { Input } from '@formily/antd'
const form = createForm()
function BasicForm() {
return (
<FormProvider form={form}>
<Field
name="username"
title="用户名"
required
component={[Input, { placeholder: '请输入' }]}
decorator={[FormItem]}
validator={{
required: true,
pattern: /^[a-z0-9_-]{4,16}$/,
message: '4-16位字母数字组合'
}}
/>
</FormProvider>
)
}
实现"选择国家→动态加载省份→根据省份显示不同字段"的典型联动:
jsx复制<Field
name="country"
component={Select}
dataSource={[
{ label: '中国', value: 'CN' },
{ label: '美国', value: 'US' }
]}
x-reactions={[
{
target: 'province',
fulfill: {
state: {
dataSource: "{{ $self.value === 'CN' ? chinaProvinces : usStates }}"
}
}
},
{
target: 'customField',
when: "{{ $self.value === 'US' }}",
fulfill: {
state: { visible: true }
}
}
]}
/>
针对大型表单的优化策略:
懒加载:
jsx复制<Field
name="section1"
x-component="Section"
x-component-props={{ lazyLoad: true }}
/>
局部更新:
javascript复制createForm({
values: initialValues,
updateStrategy: 'shallow' // 或 'deep'
})
虚拟滚动:
jsx复制<FormProvider form={form}>
<VirtualScrollFieldList height={500} itemHeight={50}>
{/* 字段列表 */}
</VirtualScrollFieldList>
</FormProvider>
根据实际项目经验,推荐以下规范:
目录结构:
code复制/forms
/schemas # JSON Schema文件
/components # 自定义表单组件
/validators # 自定义校验规则
/hooks # 公共hooks
校验规则管理:
javascript复制// validators/phone.js
export const phoneValidator = (pattern) => (value) => {
if (!value) return ''
return new RegExp(pattern).test(value)
? ''
: '手机号格式错误'
}
组件封装原则:
问题1:表单提交时部分字段未更新
解决方案:检查字段是否设置了
x-linkages但未正确触发,建议使用form.validate()代替手动校验
问题2:动态字段校验失效
调试技巧:在字段上添加
x-validator-debug属性查看校验过程
问题3:大型表单卡顿
优化方案:
- 使用
@formily/performance插件- 对复杂字段使用
React.memo- 分步加载表单数据
DevTools插件:
javascript复制import { FormilyDevTools } from '@formily/devtools'
// 在应用根节点添加
<FormilyDevTools />
日志输出:
javascript复制const form = createForm({
logger: console // 或自定义logger
})
性能分析:
javascript复制import { perf } from '@formily/perf'
perf.start()
// 操作表单后
console.log(perf.getMetrics())
从1.x迁移到2.3.0需要注意:
破坏性变更:
@formily/core的createFormAPI签名变更x-linkages改为x-reactions兼容层方案:
javascript复制import { compat } from '@formily/compat'
const form = compat(createForm)(oldConfig)
增量迁移策略:
在实际项目中,我们采用渐进式迁移方案:新功能用2.3.0开发,旧功能逐步迁移,通过配置开关控制不同版本运行。
与Redux配合使用的模式:
javascript复制const form = createForm({
values: store.getState().formData,
effects(form) {
form.subscribe(({ values }) => {
store.dispatch(updateForm(values))
})
}
})
推荐使用@formily/request处理异步:
javascript复制import { request } from '@formily/request'
const form = createForm({
effects() {
onFieldValueChange('username', async (field) => {
const res = await request('/checkUser', {
method: 'POST',
data: { username: field.value }
})
// 处理响应
})
}
})
在qiankun微前端中的使用要点:
@formily/core共享依赖@formily/scope插件开发一个支持颜色选择的组件:
jsx复制import { connect, mapProps } from '@formily/react'
const ColorPicker = ({ value, onChange }) => (
<input
type="color"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
)
export default connect(
ColorPicker,
mapProps((props) => ({
...props,
onFocus: () => props.onFocus?.(),
onBlur: () => props.onBlur?.(),
}))
)
自定义x-custom-action协议:
javascript复制import { registerAction } from '@formily/core'
registerAction('customAction', (field, params) => {
// 实现自定义逻辑
})
// 使用
{
"x-custom-action": {
"type": "customAction",
"params": {}
}
}
通过CSS-in-JS定制主题:
javascript复制import { createStyles } from '@formily/antd'
const useStyles = createStyles(({ css }) => ({
customFormItem: css`
.ant-form-item-label {
font-weight: bold;
}
`
}))
const CustomFormItem = (props) => {
const styles = useStyles()
return <FormItem {...props} className={styles.customFormItem} />
}
典型电商订单表单包含:
商品选择区:
jsx复制<Field
name="products"
type="array"
component={ProductTable}
x-reactions={[
{
target: "totalAmount",
fulfill: {
state: {
value: "{{ $self.value.reduce((sum, item) => sum + item.price * item.quantity, 0) }}"
}
}
}
]}
/>
优惠券联动:
javascript复制{
"x-reactions": [
{
"dependencies": ["coupon"],
"fulfill": {
"run": "{{ $deps[0] ? applyCoupon($deps[0]) : resetDiscount() }}"
}
}
]
}
测试字段联动逻辑:
javascript复制test('coupon should apply discount', async () => {
const form = createForm({
values: { total: 100, coupon: 'OFF50' }
})
await form.validate()
expect(form.values.discount).toBe(50)
})
使用Cypress测试表单流程:
javascript复制describe('Order Form', () => {
it('should submit successfully', () => {
cy.visit('/order')
cy.get('[name="products[0].sku"]').select('SKU123')
cy.get('[name="coupon"]').type('WELCOME10')
cy.contains('提交').click()
cy.url().should('include', '/success')
})
})
根据Formily团队的roadmap,后续版本将重点关注:
在实际项目中,我们已经开始尝试将Formily与低代码平台结合,通过拖拽方式生成表单配置,再由Formily渲染执行。这种模式在内部工具开发中效率提升显著。