- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
本指南以 @formily/vue 官方引导文档为主体,系统讲解它如何作为「渲染胶水层」将 @formily/core 的响应式 ViewModel 与 Vue 组件绑定起来,覆盖核心定位、超高性能机制、开箱即用的模型绑定、基于标准 JSON-Schema 的协议驱动,以及 Template / JSON Schema / Markup Schema 三种开发模式的完整实战代码。读完本文,你将理解 @formily/vue 的架构分层与上下文注入机制,掌握 Field 系组件的接入方式和 SchemaField 协议渲染的递归原理,并能直接在 Vue 2 / Vue 3 项目中落地三种开发模式。
核心定位:不管理数据、只做绑定的「渲染胶水层」
@formily/vue 的核心定位是将 ViewModel(即 @formily/core 创建的 Form 实例)与组件实现一个状态绑定关系。它不负责管理表单数据,不负责表单校验——这些职责全部由 @formily/core 承担,@formily/vue 仅仅是渲染胶水层(glue layer)。
但这层胶水并不「脏」:它通过标准化的绑定关系,把大量脏逻辑(组件事件转发、字段状态同步、装饰器与组件的渲染组装等)优雅解耦,从而让业务代码保持可维护。从 MVVM 角度看,@formily/core 就是 ViewModel,Component 与 Decorator 就是 View,@formily/vue 负责完成二者之间的绑定,这层绑定关系在官方概念文档中被称为「模型绑定」,具体可参见 packages/vue/docs/guide/concept.md 与 core 侧的 MVVM 指南。
在源码层面,这一绑定逻辑集中在 ReactiveField.ts:它创建并持有字段模型,把字段的value注入组件的attrs.value,并把组件的change/focus/blur事件转发给字段模型的onInput/onFocus/onBlur方法,同时根据pattern(editable/disabled/readOnly/readPretty)自动设置组件的disabled与readOnly属性。整个组件被observer包裹,任何字段模型变化都会触发精确的最小化重渲染。
五大核心特性
1. 超高性能:依赖追踪、精确更新、按需渲染
借助 @formily/core 的响应式模型(packages/reactive),@formily/vue 无需额外优化即可获得高性能优势:依赖追踪、精确更新、按需渲染。字段模型之间相互独立,一个字段的输入、校验、显隐变化只会触发自身及其真正依赖它的视图更新,表单开发只需关注业务逻辑,无需考虑性能优化。
这一机制在源码中有直接体现:ReactiveField.ts 使用shallowRef持有字段实例,渲染函数通过observer(来自 @formily/reactive-vue)建立细粒度的响应式依赖,只有字段模型中被实际读取的属性变化才会触发重渲染。
2. 开箱即用:Field 系组件 + 双向绑定约定
@formily/vue 提供了一系列 Vue 组件:Field/ArrayField/ObjectField/VoidField。使用时只需给 Field 组件传入component属性(组件引用 + 属性数组),组件只要支持value/@change这样的双向绑定约定,即可快速接入,接入成本极低。例如:
<template> <FormProvider :form="form"> <Field name="input" :component="[Input, { placeholder: '请输入' }]" /> </FormProvider> </template>component支持两种写法:单个组件引用(如:component="Input"),或[组件引用, 属性对象]的元组形式(如:component="[Input, { placeholder: '请输入' }]")。字段模型的value与组件的value自动同步,组件的@change事件自动写入字段模型。
3. 协议驱动:标准 JSON-Schema
@formily/vue 提供SchemaField这样的协议驱动组件,底层基于标准 JSON-Schema 驱动,同时扩展了x-*属性来表达 UI。这让表单开发变得动态化、可配置化,甚至可以做到一份协议,多端渲染表单(React、Vue 等不同端共享同一份 Schema 描述)。
4. 场景复用:协议片段抽象场景组件
借助协议驱动能力,可以将携带业务逻辑的协议片段抽象成场景组件,帮助用户在某些场景下高效开发,例如FormTab、FormStep这类场景组件——它们本质上是对协议结构(x-component与x-content等)的封装复用。
5. 智能提示:完全 TypeScript
formily 是完全的 TypeScript 项目,用户在 VSCode 或 WebStorm 等 IDE 中开发可以获得最大化的智能提示体验,包括组件 Props 类型提示、Schema 字段提示等。packages/vue/package.json 中types字段声明了类型产物入口type-artefacts/cur/index.d.ts,并分别针对 Vue 2 / Vue 3 构建类型。
核心架构:三类上下文与统一下发
@formily/vue 的架构相比 @formily/core 并不复杂(完整架构图见 packages/vue/docs/guide/architecture.md)。它支持两类用户:
- 纯源码开发用户:直接使用
Field/ArrayField/ObjectField/VoidField组件; - JSON-Schema 动态开发用户:依赖
SchemaField组件。
两类用户都必须使用FormProvider组件统一下发上下文;而SchemaField内部实际依赖的仍是 Field/ArrayField/ObjectField/VoidField 组件(见 RecursionField.ts 中按 Schema 的type分发到对应字段组件的逻辑)。
架构中包含三个层次的上下文,官方概念文档对此有明确说明(见 packages/vue/docs/guide/concept.md):
| 上下文 | 提供者 | 读取方式 | 用途 |
|---|---|---|---|
| 表单上下文(FormContext) | FormProvider | useForm | 读取 @formily/core 创建的 Form 实例 |
| 字段上下文(FieldContext) | Field/ArrayField/ObjectField/VoidField | useField | 读取当前字段模型,便于模型映射 |
| 协议上下文(FieldSchemaContext) | RecursionField | useFieldSchema | 读取当前字段的 Schema 描述(仅限 SchemaField / RecursionField 子树内) |
在源码中,上下文通过 Vue 的 provide/inject 实现:FormProvider.ts 使用provide(FormSymbol, formRef)下发表单实例,ReactiveField.ts 使用provide(FieldSymbol, fieldRef)下发字段实例,RecursionField.ts 使用provide(SchemaSymbol, fieldSchemaRef)下发 Schema 实例。相关的 Hooks 实现位于 packages/vue/src/hooks,包括useForm、useField、useFieldSchema、useFormEffects、useParentForm。
模型绑定:ViewModel 与 View 的桥接手段
模型绑定指将 @formily/core 的 ViewModel 与 Vue 组件(View)绑定起来的过程,主要实现手段有:
- useField:在自定义组件中读取当前字段模型;
- connect:连接组件与字段,支持级联应用多个 mapper;
- mapProps:将字段模型的指定属性映射到组件 props。
connect/mapProps/mapReadPretty的实现位于 packages/vue/src/shared/connect.ts:
mapProps(...mappers)通过FormPath.getIn(field, extract)从字段模型中提取属性值,写入组件的目标 prop;当提取的 key 是value且映射目标不是value时,会删除原始valueprop,避免冲突。mapReadPretty(component, readPrettyProps)包装组件:当field.pattern === 'readPretty'时切换为传入的只读展示组件,否则仍渲染原组件。connect(target, ...mappers)依次将 mapper 应用到目标组件,最终用observer包裹为响应式组件(Vue 2 使用 functional 组件,Vue 3 使用setup返回渲染函数)。
需要注意的是:Component 只需支持value/onChange属性,即可自动实现数据层的双向绑定。ReactiveField 在渲染时会自动为组件附加value(来自字段模型)以及change/focus/blur事件处理器(内部调用field.onInput/field.onFocus/field.onBlur),这就是「开箱即用」的底层机制。
协议驱动:Schema、递归渲染与协议绑定
协议驱动渲染是 @formily/vue 中学习成本最高的部分,但掌握后收益也最高。核心包含 4 个概念:Schema、递归渲染、协议绑定、三种开发模式。
Schema
formily 的协议驱动基于标准 JSON Schema,并在标准之上扩展了x-*属性来表达 UI(如x-component、x-component-props、x-decorator、x-decorator-props、x-validator、x-reactions、x-content、x-visible、x-display等),使协议具备完整描述复杂表单的能力。完整的 Schema 协议与字段模型映射关系参见 packages/vue/docs/api/shared/schema.md。
递归渲染
递归渲染是指组件 A 在某些条件下继续用组件 A 来渲染内容。由于 JSON-Schema 本身是递归型结构,RecursionField渲染时会从顶层 Schema 节点解析:非 object / array 类型直接渲染具体组件;object 类型则遍历properties,继续用 RecursionField 渲染子级 Schema 节点。以下伪代码展示了递归过程(渲染权标注在各行注释中):
{ "type": "object", "properties": { "username": { "type": "string", "x-component": "Input" }, "phone": { "type": "string", "x-component": "Input", "x-validator": "phone" }, "email": { "type": "string", "x-component": "Input", "x-validator": "email" }, "contacts": { "type": "array", "x-component": "ArrayTable", "items": { "type": "object", "properties": { "username": { "type": "string", "x-component": "Input" }, "phone": { "type": "string", "x-component": "Input", "x-validator": "phone" }, "email": { "type": "string", "x-component": "Input", "x-validator": "email" } } } } } }@formily/vue 递归渲染的入口是SchemaField,但其内部实际使用RecursionField渲染(见 SchemaField.ts 中children.push(h(RecursionField, ...)))。特殊之处在于array 类型自增列表:由于自增列表的 UI 个性化定制程度很高,递归渲染权交给用户——需要在自定义组件(如 ArrayTable)内部手动使用 RecursionField 进行递归渲染。
SchemaField与RecursionField的差别主要有两点(官方概念文档原述):
- SchemaField 支持 Markup 语法,会提前解析 Markup 语法生成 JSON Schema 移交给 RecursionField 渲染;而 RecursionField 只能基于 JSON Schema 渲染;
- SchemaField 渲染整体的 Schema 协议,RecursionField 渲染局部的 Schema 协议。
从源码看,RecursionField.ts 会根据 Schema 的type值分发:object渲染ObjectField、array渲染ArrayField、void渲染VoidField,其余类型渲染Field;并通过onlyRenderProperties/onlyRenderSelf/mapProperties/filterProperties等 props 控制递归渲染的范围与转换。
协议绑定
协议绑定是将 Schema 协议转换成模型绑定的过程。JSON-Schema 是 JSON 字符串、可离线存储,而模型绑定是内存间的运行时(Runtime)绑定关系。例如x-component在 Schema 中是组件的字符串标识,在字段模型中则是组件引用,因此 JSON 字符串与 Runtime 层需要转换。RecursionField 中通过schema.toFieldProps({ ...options, scope })(见 RecursionField.ts 的getPropsFromSchema)完成「Schema → 字段模型属性」的转换,options.components中的组件注册表则完成「字符串标识 → 组件引用」的解析(ReactiveField 渲染时用FormPath.getIn(options?.components, field.componentType)查找组件)。
总结:@formily/vue 中存在2 层绑定关系——Schema 绑定模型、模型绑定组件,实现绑定的胶水层就是 @formily/vue。需要注意,Schema 绑定字段模型之后,字段模型感知不到 Schema:例如要修改enum,实际是修改字段模型中的dataSource属性。字段模型的操作方式参考 core 的 Field 模型文档。
三种开发模式实战
@formily/vue 面向不同用户提供三种开发模式,完整对比与示例见 packages/vue/docs/guide/concept.md。
Template 开发模式
该模式主要使用Field/ArrayField/ObjectField/VoidField组件,适合纯源码开发:
<template> <FormProvider :form="form"> <Field name="input" :component="[Input, { placeholder: '请输入' }]" /> </FormProvider> </template> <script> import { Input } from 'ant-design-vue' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import 'ant-design-vue/dist/antd.css' export default { components: { FormProvider, Field }, data() { return { Input, form: createForm(), } }, } </script>createForm()来自 @formily/core,返回的 Form 实例是表单唯一的 ViewModel;FormProvider负责将其下发给所有子组件。
JSON Schema 开发模式
该模式将 JSON Schema 直接传给SchemaField的schema属性,Schema 可以是离线存储、动态下发的数据,适合可配置化 / 低代码场景:
<template> <FormProvider :form="form"> <SchemaField :schema="schema" /> </FormProvider> </template> <script> import { Input } from 'ant-design-vue' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import 'ant-design-vue/dist/antd.css' const { SchemaField } = createSchemaField({ components: { Input, }, }) export default { components: { FormProvider, SchemaField }, data() { return { form: createForm(), schema: { type: 'object', properties: { input: { type: 'string', 'x-component': 'Input', 'x-component-props': { placeholder: '请输入', }, }, }, }, } }, } </script>注意:createSchemaField({ components })工厂函数负责注册协议中用到的组件映射(字符串x-component标识 → 实际组件),返回的SchemaField才能正确解析协议。源码见 SchemaField.ts,组件注册表会与SchemaField的componentsprop 合并(options.components与props.components合并后下发)。
Markup Schema 开发模式
该模式是对源码开发更友好的 Schema 开发模式,同样使用 SchemaField 相关组件。主要有以下特点:
- 主要依赖
SchemaStringField/SchemaArrayField/SchemaObjectField等描述标签来表达 Schema; - 每个描述标签都代表一个 Schema 节点,与 JSON-Schema 等价;
- SchemaField 子节点不能随意插入 UI 元素——SchemaField 只会解析子节点的所有 Schema 描述标签并转换成 JSON Schema,最终交给 RecursionField 渲染;如需插入 UI 元素,可在
SchemaVoidField上使用x-content属性。
<template> <FormProvider :form="form"> <SchemaField> <SchemaStringField x-component="Input" :x-component-props="{ placeholder: '请输入' }" /> <div>我不会被渲染</div> <SchemaVoidField x-content="我会被渲染" /> <SchemaVoidField :x-content="Comp" /> </SchemaField> </FormProvider> </template> <script> import { Input } from 'ant-design-vue' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import 'ant-design-vue/dist/antd.css' const SchemaComponents = createSchemaField({ components: { Input, }, }) const Comp = { render(h) { return h('div', ['我也会被渲染']) }, } export default { components: { FormProvider, ...SchemaComponents }, data() { return { form: createForm(), Comp, } }, } </script>从源码看,createSchemaField会额外生成SchemaStringField、SchemaObjectField、SchemaArrayField、SchemaBooleanField、SchemaDateField、SchemaDateTimeField、SchemaVoidField、SchemaNumberField等标记组件(见 SchemaField.ts 的SchemaFieldFactory),它们统一包装内部的MarkupField:把标签上的属性解析为 Schema 节点,添加到父级 Schema 的properties(object/void 类型)或items(array 类型)中,最终整个 Markup 结构被转换为一份 JSON Schema 交给 RecursionField 渲染。x-*系列属性(如x-component、x-decorator、x-validator、x-reactions、x-visible、x-disabled、x-read-pretty等)以及标准 JSON-Schema 关键字(enum、pattern、maximum、required等)都作为标记组件的 props 被解析。
安装与快速开始
@formily/vue通过vue-demi同时兼容 Vue 2 与 Vue 3(peerDependencies 声明vue: ^2.6.0 || >=3.0.0-rc.0,见 packages/vue/package.json),官方安装命令如下:
Vue 3:
$ npm install --save @formily/core @formily/vueVue 2(额外需要@vue/composition-api):
$ npm install --save @formily/core @formily/vue @vue/composition-api安装后即可按上文三种开发模式任一方式快速开始。完整可运行的入门 Demo 位于 packages/vue/docs/demos/index.vue,各 API 组件的独立示例分布在 packages/vue/docs/demos/api 目录(对应field、schema-field、array-field、object-field、void-field、form-provider、form-consumer、recursion-field、connect、map-props等)。官方 API 文档入口见 packages/vue/docs/api,共覆盖 11 个组件、5 个 Hooks 与 6 个共享工具(connect、mapProps、mapReadPretty、observer、schema、injections)。
状态可观测:对接 Formily DevTools
安装 FormilyDevtools 扩展可以实时观测模型状态变化、排查问题。本仓库中的 DevTools 源码位于 devtools/chrome-extension,其通过 content script 与后台脚本注入、页面内inject.ts通信,读取表单与字段模型的响应式状态树(见 devtools/chrome-extension/src/extension/inject.ts 与 devtools/chrome-extension/src/extension/backend.ts)。配合 @formily/vue 的observer精确渲染机制,DevTools 可以实时展示字段的值、校验状态、显隐状态与依赖关系,帮助开发者快速定位复杂联动场景下的问题。
小结
@formily/vue 的设计哲学是「职责分离」:把数据、校验、联动交给 @formily/core 的响应式模型,把自己定位为纯粹的渲染胶水层,通过 FormProvider 统一上下文、Field 系组件完成模型绑定、SchemaField/RecursionField 完成协议驱动。理解三类上下文、两层绑定关系与三种开发模式,即可在 Vue 项目中按需选择最合适的表单开发方式——无论是追求零成本的 Template 模式、面向动态化的 JSON Schema 模式,还是兼顾源码友好与协议能力的 Markup Schema 模式。
- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
相关推荐
Formily 的 MVVM 架构定位:从 OOP 到 FP,理解表单响应式模型的底层设计
Formily 的 MVVM 架构定位:从 OOP 到 FP,理解表单响应式模型的底层设计 导读 本文基于 Formily 官方内核文档 mvvm.zh CN.
前端UI组件Mantle设计模式:面向协议的模型层架构
Mantle设计模式:面向协议的模型层架构 你是否还在为iOS开发中的JSON解析和模型转换编写大量重复代码?是否在面对复杂数据模型时感到维护困难?Mantle
移动开发Formily React 指南:@formily/react 的架构、模型绑定与三种开发模式
Formily React 指南:@formily/react 的架构、模型绑定与三种开发模式 导读 @formily/react 是 Formily 表单方案
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考