FormKit 的架构是一种 JSON 可序列化的数据格式,用于存储 DOM 结构和组件实现,包括 FormKit 表单。虽然专门为实现和生成表单而创建,但该格式能够生成任何 HTML 标记或使用任何第三方组件。
架构通过 FormKit 的 <FormKitSchema> 组件进行渲染,该组件默认情况下不会全局注册。您需要导入它:
import { FormKitSchema } from '@formkit/vue'
FormKit 内置了使用架构生成表单的一流支持。这使得可以在数据库、文件甚至二维码中存储生成的表单!要生成表单,请将您的架构数组传递给 <FormKitSchema> 组件,使用 :schema 属性。
<FormKitSchema :schema="yourSchemaArray" />
让我们来看一个快速的例子:
我们在上面看到了许多功能,包括 $el 和 $cmp 属性,$formkit 属性简写,验证,条件渲染,标签,帮助文本和多种类型的输入。我们将在本页的其余部分解开所有这些功能。
架构是由对象或字符串(称为“架构节点”)组成的数组,其中每个数组项定义了一个单独的架构节点。有 3 种类型的架构节点:
$el 属性定义。$cmp 属性定义。$formkit 属性定义。完整 $cmp 格式的语法糖。架构支持高级功能,如条件逻辑、布尔运算符、循环、插槽和数据作用域 — 所有这些都保证可以序列化为字符串。
HTML 元素使用 $el 属性定义。您可以使用 $el 渲染任何 HTML 元素。属性可以通过 attrs 属性添加,内容则通过 children 属性分配:
请注意,在上面的示例中,style 属性是独特的,它应该定义为一个样式到值对的对象,而不是字符串。
可以使用 $cmp 属性定义组件。$cmp 属性应该是一个字符串,它引用一个全局定义的组件或者通过 library 属性传入 FormKitSchema 的组件:
为了通过 library 属性传递具体的组件,最好使用 Vue 的 markRaw 方法 来包装你的库。
除了 schema 数组(和可选的 library)之外,FormKitSchema 对象还可以包括一个 data 属性。然后可以在你的 schema 中直接引用 data 对象的值 —— 并且你的 schema 将保持原始数据对象的响应性。
要引用 data 对象中的值,你只需使用美元符号 $ 后跟数据对象中的属性名。引用可以用在 attrs、props、条件判断以及作为 children:
请注意,在上面的示例中,我们使用数组来连接 "Hello" 和 "$location"。我们这样做是因为在 schema 中的数据引用和逻辑表达式必须始终以美元符号 $ 开头 —— 否则它们将被视为未解析的字符串字面量。
Schemas 支持调用原始引用数据中的函数 —— 你甚至可以将数据引用作为该函数的参数传递!
就像 JavaScript 一样 —— 你可以使用点语法 object.property 来访问深层嵌套对象的属性:
Schema 引用可以具有任何结构或属性,但在数据引用对象的根部有 2 个保留字: $slots 和 $get。
Schemas 还支持布尔逻辑、比较和算术表达式形式的逻辑。这些表达式可以用在任何可以使用数据引用的地方(attrs、props、条件判断和 children):
表达式必须始终以 $ 开头。如果表达式的第一个元素是数据引用(例如:$count + 2),那么它已经以 $ 开头,不需要进一步的标记。然而,表达式的第一个字符通常不是美元符号 —— 这些表达式需要用 $: 来“标记” —— 例如 $: ($count * 3) - 7。
虽然它看起来非常像 JavaScript —— schema 表达式并不是 JavaScript。它们更像是一种模板语言。表达式在 setup 时被编译为功能性的 JavaScript,但语法与 JavaScript 不是 1-1 兼容的。这提高了性能,并提供了一个关键的安全层,因为只有明确暴露的数据和功能才能被执行。
Schema 表达式仅限于以下运算符和括号:
| 运算符 | 用例 |
|---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取模 |
&& | 布尔与 |
|| | 布尔或 |
=== | 严格相等 |
!== | 严格不相等 |
== | 宽松相等 |
!= | 宽松不相等 |
>= | 大于或等于 |
<= | 小于或等于 |
> | 大于 |
< | 小于 |
FormKit 架构可以利用引用和表达式使架构节点和属性具有条件性。这些条件可以通过两种方式添加:
$el 和 $cmp 节点上的 if 属性。if/then/else 对象if 属性$el 和 $cmp 架构节点都可以利用一个 if 属性,这与 Vue 中的 v-if 大致相当。如果分配给 if 属性的表达式为真值,节点就会被渲染,否则不会:
使用条件或迭代(使用 if 或 for)的架构节点应始终包含一个显式的 key 属性。如果没有这个属性,Vue 可能会重用上一次渲染的 DOM 节点,这可能导致意外行为、错误和性能问题。
if/then/else 对象if/then/else 对象允许更复杂的条件逻辑。它可以用来有条件地渲染节点、架构节点列表、attrs 对象的值或 props 对象的值。也可以嵌套 if/then/else 对象以创建更复杂的结构 — 类似于 JavaScript 中的 else if 语句。
if/then/else你可以在通常使用架构节点的任何地方使用 if/then/else 对象。这包括根架构数组,或另一个架构节点的 children 属性:
使用条件或迭代(使用 if 或 for)的架构节点应始终包含一个显式的 key 属性。如果没有这个属性,Vue 可能会重用上一次渲染的 DOM 节点,这可能导致意外行为、错误和性能问题。
if/then/else你也可以使用 if/then/else 语句来有条件地输出 attrs 或 props 的值:
$el 和 $cmp 架构节点都支持循环。循环语法类似于 Vue 中的 v-for,它期望一个对象或数组来迭代,并分配一个属性以赋值当前迭代的值。你还可以选择捕获当前迭代的索引或属性:
使用条件或迭代(使用 if 或 for)的架构节点应始终包含一个显式的 key 属性。如果没有这个属性,Vue 可能会重用上一次渲染的 DOM 节点,这可能导致意外行为、错误和性能问题。
架构可以在架构中的任何位置渲染 <FormKitSchema> 组件的插槽内容,只要是普通架构节点可以渲染的地方。所有作用域插槽都会自动提供给架构,作为 $slots 引用对象:
有时可能需要将一个变量或未知属性或 props 的对象传递给 $cmp 或 $el。在 Vue 中我们会使用 v-bind — 在架构中我们使用 bind 属性:
有时可能需要防止给定的属性或 prop 被解析。这可以通过在属性或 prop 前加上 __raw__ 来完成:
{
$cmp: 'PriceComponent',
props: {
__raw__price: '$2.99'
}
}
在上面的例子中,__raw__ 前缀将被移除,未解析的值 $2.99 将作为 price prop 传递给 PriceComponent。
另一个涉及此情况的场景是渲染 FormKit 组件。<FormKit> 组件有一个 sections-schema prop,允许用户传递架构片段以与输入的各个部分合并。在这种边缘情况下,我们希望将架构块作为原始 JavaScript 对象传递给 <FormKit> 组件。为此,我们再次在 sectionsSchema prop 前加上 __raw__:
注意,如果你从上面的例子中移除 __raw__ 前缀,前缀将不再有效 — 这是因为在创建组件时已经解析了 sectionsSchema prop 的值,而不是作为 JavaScript 对象传递。
虽然架构几乎可以用于任何目的 — 主要目标是赋予开发者使用可序列化的数据格式构建复杂和动态表单的能力。使用架构与 FormKit 输入很好地覆盖了这个用例。
假设你已经全局注册了 FormKit 组件 — 你可以使用 $cmp 类型的架构节点从架构中渲染你的 FormKit 输入:
虽然 cmp 语法是通用的,适用于任何 Vue 组件,但对于 FormKit 输入来说,它有些冗长。为了简化这个过程,FormKit 支持第四种节点类型 $formkit,它是完整的 $cmp 格式的语法糖。
使用 $formkit 简写时,props 对象会被扁平化,与顶层属性($formkit 的同级)合并。例如:
模式格式有一个内置函数专门用于 FormKit 输入:$get 函数。这个内置函数允许模式访问任何其他 FormKit 输入的上下文对象(即使是在当前表单之外的)—— 前提是该输入明确声明了 id 属性。这允许模式根据你自己输入的状态有条件地做出响应:
要渲染一个表单元素,你可以使用 $formkit: 'form' 模式节点,或者在 <FormKitSchema> 组件外面包裹一个 <FormKit type="form"> 组件: