ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Formily 解构路径(Destructor Path):前后端数据结构差异的优雅兼容方案

Formily 解构路径(Destructor Path):前后端数据结构差异的优雅兼容方案 Formily 解构路径Destructor Path前后端数据结构差异的优雅兼容方案【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址: https://gitcode.com/gh_mirrors/fo/formily前后端数据结构不匹配是表单开发中最常见的痛点之一前端日期范围选择器输出的是数组后端接口要求的却是startDate、endDate两个扁平字段。本文以 Formily 的路径解构能力为核心结合 解构器源码 与测试用例讲解如何在 Markup Schema、JSON Schema、纯 JSX 三种模式下用一条解构路径如[startDate,endDate]零成本完成字段拆分与重组让前端不再为后端领域模型消化不平等条约。痛点前端结构友好与后端模型约束的矛盾在很多业务场景中前后端数据结构不匹配的问题看似简单解决起来却十分难受最常见的例子是前端日期范围组件如DatePicker.RangePicker输出的是数组结构[2020-11-20, 2021-12-30]后端接口要求的却是拆分扁平结构{ startDate: 2020-11-20, endDate: 2021-12-30 }。这种差异很大程度上受后端领域模型限制——从后端模型设计角度拆分扁平结构是最佳方案但从前端组件化角度看数组结构又最自然。两边各有道理结果往往只能由前端反复写取值、拆字段的胶水代码来消化。Formily 提供了一种全新的解决思路解构路径Destructor Path。它允许字段的name直接写成解构表达式如[startDate,endDate]Formily 的路径系统会在表单值与字段地址之间自动完成数组 → 扁平字段的转换无需任何额外的 transform 代码。解构路径的基本形态解构路径语法借鉴了 JavaScript 的解构赋值并在此基础上做了扩展支持以下几种形态形态写法语义数组解构[startDate,endDate]数组元素依次映射到扁平字段对象解构{startDate,endDate}对象属性映射到扁平字段重命名{startDate:beginDate}源字段名与目标字段名不同嵌套解构{range:{start:beginDate,end:endDate}}深层嵌套结构拍平混合解构[{startDate,endDate},endDate]数组与对象混合从源码结构看types.ts 中定义了DestructorRule包含key与path两个属性解构表达式在解析阶段会被拆解为一组规则key是目标扁平字段名path是源数据中的取值路径。而 destructor.ts 中的parseDestructorRules正是负责把 AST 节点ObjectPattern/ArrayPattern递归转换成这组规则的实现。Markup Schema 案例以下案例使用 Formily 的 Markup Schema 模式通过onFieldValueChange联动控制解构字段的显示状态import React from react import { Form, FormItem, DatePicker, FormButtonGroup, Radio, Submit, } from formily/antd import { createForm, onFieldValueChange } from formily/core import { createSchemaField, FormConsumer } from formily/react const SchemaField createSchemaField({ components: { FormItem, DatePicker, Radio, }, }) const form createForm({ effects() { onFieldValueChange(visible_destructor, (field) { form.setFieldState([startDate,endDate], (state) { state.visible !!field.value }) }) }, }) export default () { return ( Form form{form} layoutvertical SchemaField SchemaField.Boolean namevisible_destructor title是否显示解构字段 default{true} enum{[ { label: 是, value: true }, { label: 否, value: false }, ]} x-decoratorFormItem x-componentRadio.Group / SchemaField.String nameundestructor title解构前 x-decoratorFormItem x-componentDatePicker.RangePicker / SchemaField.String name[startDate,endDate] title解构后 default{[2020-11-20, 2021-12-30]} x-decoratorFormItem x-componentDatePicker.RangePicker / /SchemaField code pre FormConsumer {(form) JSON.stringify(form.values, null, 2)} /FormConsumer /pre /code FormButtonGroup Submit onSubmit{console.log}提交/Submit /FormButtonGroup /Form ) }要点说明name[startDate,endDate]是关键字段地址是数组解构表达式但组件依然是DatePicker.RangePicker用户交互体验不变提交时表单值会自动变成{ startDate: 2020-11-20, endDate: 2021-12-30 }直接满足后端接口要求注意这里setFieldState的目标也使用了解构路径[startDate,endDate]说明解构路径不仅可以作为字段的name还能作为查询、操作字段的地址使用。JSON Schema 案例如果表单结构来自后端动态下发或 JSON 配置可以使用 JSON Schema 模式用x-reactions完成同样的联动逻辑import React from react import { Form, FormItem, DatePicker, FormButtonGroup, Radio, Submit, } from formily/antd import { createForm } from formily/core import { createSchemaField, FormConsumer } from formily/react const SchemaField createSchemaField({ components: { FormItem, DatePicker, Radio, }, }) const form createForm() const schema { type: object, properties: { visible_destructor: { type: boolean, title: 是否显示解构字段, default: true, enum: [ { label: 是, value: true }, { label: 否, value: false }, ], x-decorator: FormItem, x-component: Radio.Group, }, undestructor: { type: string, title: 解构前, x-decorator: FormItem, x-component: DatePicker.RangePicker, }, [startDate,endDate]: { type: string, title: 解构后, default: [2020-11-20, 2021-12-30], x-decorator: FormItem, x-component: DatePicker.RangePicker, x-reactions: { dependencies: [visible_destructor], fulfill: { state: { visible: {{!!$deps[0]}}, }, }, }, }, }, } export default () { return ( Form form{form} layoutvertical SchemaField schema{schema} / code pre FormConsumer {(form) JSON.stringify(form.values, null, 2)} /FormConsumer /pre /code FormButtonGroup Submit onSubmit{console.log}提交/Submit /FormButtonGroup /Form ) }要点说明属性名[startDate,endDate]直接作为 JSON Schema 的properties键是解构路径在声明式配置中的典型用法x-reactions通过dependencies: [visible_destructor]声明依赖配合fulfill.state.visible: {{!!$deps[0]}}表达式实现联动适合 Schema 由接口下发、无法写代码的场景default使用数组[2020-11-20, 2021-12-30]与解构后扁平化存储的表单值形成鲜明对比正好展示组件视角数组、数据视角扁平的双轨模型。纯 JSX 案例如果偏好完全命令式的写法可以直接使用Field组件与reactions函数import React from react import { Form, FormItem, DatePicker, FormButtonGroup, Radio, Submit, } from formily/antd import { createForm } from formily/core import { Field, FormConsumer } from formily/react const form createForm() export default () { return ( Form form{form} layoutvertical Field namevisible_destructor title是否显示解构字段 initialValue{true} dataSource{[ { label: 是, value: true }, { label: 否, value: false }, ]} decorator{[FormItem]} component{[Radio.Group]} / Field nameundestructor title解构前 decorator{[FormItem]} component{[DatePicker.RangePicker]} / Field name[startDate,endDate] title解构后 initialValue{[2020-11-20, 2021-12-30]} decorator{[FormItem]} component{[DatePicker.RangePicker]} reactions{(field) { field.visible !!field.query(visible_destructor).value() }} / code pre FormConsumer {(form) JSON.stringify(form.values, null, 2)} /FormConsumer /pre /code FormButtonGroup Submit onSubmit{console.log}提交/Submit /FormButtonGroup /Form ) }要点说明reactions回调中通过field.query(visible_destructor)查询联动字段的值写法直观、便于调试initialValue{[2020-11-20, 2021-12-30]}以数组形式初始化解构路径会自动将其拆分为扁平字段存储。解构路径的底层原理Formily 的解构能力并不局限于表单层它由独立的 formily/path 包提供核心是一套路径解析、匹配与读写机制。当路径中出现[、{等字符时Parser 会将其识别为DestructorExpression解构表达式再交由parseDestructorRules解析为DestructorRule[]规则集并通过DestructorCache做内存缓存见 destructor.ts。读写数据的核心逻辑如下getInByDestructor读取时从源对象中按key取值再按path组装回嵌套结构实现扁平 → 嵌套setInByDestructor写入时从值中按path取值再按key写回扁平字段实现嵌套 → 扁平deleteInByDestructor/existInByDestructor提供对应的删除与存在性判断能力。这些函数被 index.ts 中的Path.getIn/Path.setIn/Path.deleteIn静态方法串联起来字段写入时会自动判断当前段是否为解构规则并切换到对应分支。在 accessor.spec.ts 中可以看到大量针对解构的测试用例例如test(test getIn with destructor, () { const value { array: [{ aa: 123, bb: 321 }] } expect(getIn(value, array.0.[aa,bb])).toEqual([123, 321]) })这条用例验证了读取方向的行为路径array.0.[aa,bb]会先从value.array[0]取出{ aa: 123, bb: 321 }再按解构规则将其还原为数组[123, 321]。而对应的setIn测试则验证了写入方向的反向转换。进阶对象解构、重命名与嵌套除了文档示例中的数组解构formily/path 还支持更丰富的解构形态可直接用于表单字段命名对象解构与重命名// 源字段名 aaa 映射到目标字段 ooo重命名 Path.setIn({}, a.b.c.{aaa:ooo,bbb}, { aaa: 123, bbb: 321 }) // { a: { b: { c: { ooo: 123, bbb: 321 } } } } // 读取方向自动还原 Path.getIn({ a: { b: { c: { ooo: 123, bbb: 321 } } } }, a.b.c.{aaa:ooo,bbb}) // { aaa: 123, bbb: 321 }数组与对象混合Path.setIn({}, a.b.c.[{ddd,kkk:mmm},bbb], [{ ddd: 123, kkk: hhh }, 321]) // { a: { b: { c: { ddd: 123, bbb: 321, mmm: hhh } } } }复杂嵌套拍平Path.setIn( {}, {aa:{bb:{cc:destructor1,dd:[destructor2,destructor3],ee}}}, { aa: { bb: { cc: 123, dd: [333, 444], ee: abcde } } } ) // { destructor1: 123, destructor2: 333, destructor3: 444, ee: abcde }从上述测试可以看出解构路径本质上是一种结构描述语言写入方向负责把后端下发或任意嵌套的值解构为表单所需的扁平字段读取方向则把表单值重组还原。这让前端可以自由选择对组件最友好的数据形态同时保证提交给后端的数据结构完全受控。适用场景与注意事项典型适用场景日期范围、时间段等天然成对的数据RangePicker输出数组接口需要startDate/endDate后端返回嵌套对象、前端需要扁平字段参与联动校验的场景需要把多个组件值聚合提交到同一个后端字段的场景利用读取方向的重组能力。注意事项解构路径对字段地址的读写是双向的使用form.values获取到的始终是解构后的扁平结构setFieldState、query等 API 中也应使用解构地址来定位字段解构表达式中不建议包含空格等冗余字符Formily 在解析时会自动去除见 parser.ts 中node.source的replace(/\s*/g, )处理解构规则在首次解析后会被缓存DestructorCache、pathCache因此大量使用解构路径也不会带来明显的重复解析开销需要更复杂的路径能力如通配匹配、范围匹配、排除模式时可以参考 match.spec.ts 中的用例解构表达式同样可以嵌入到匹配路径中使用。通过解构路径Formily 把前后端数据差异兼容从一段段手写的转换函数抽象成了声明式的路径语法无论你使用 Markup Schema、JSON Schema 还是纯 JSX都能以最低成本完成数据结构的前后端对齐。【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址: https://gitcode.com/gh_mirrors/fo/formily创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表