JSON Schema驱动表单的核心设计思路
中后台系统大量表单页面的重复开发是前端效能损耗的重灾区。JSON Schema驱动表单的思路是将表单结构、校验规则、联动逻辑全部用JSON描述,运行时由统一的渲染引擎解析JSON并生成对应的Vue3组件树。新增表单只需编写JSON配置,无需新建Vue文件。
核心架构分为三层:Schema层定义表单元数据,Parser层将Schema解析为内部组件树描述,Renderer层根据组件树描述渲染为实际的Vue3组件。三层解耦后,Schema可来自接口动态下发,Parser可扩展自定义字段类型,Renderer可替换为不同UI组件库。
Schema结构定义与类型约束
表单Schema需要覆盖字段类型、默认值、校验规则、联动条件等要素:
interface FieldSchema {
field: string
type: 'input' | 'select' | 'datepicker' | 'number' | 'switch' | 'slot'
label: string
default?: any
required?: boolean
rules?: ValidationRule[]
props?: Record<string, any>
visible?: ConditionExpr
disabled?: ConditionExpr
options?: OptionItem[] | (() => Promise<OptionItem[]>)
}
interface FormSchema {
fields: FieldSchema[]
layout?: { columns: number; gap: string }
submit: { label: string; action: string }
}
ConditionExpr支持简单的条件联动:当目标字段等于特定值时控制当前字段的可见性。复杂联动可通过slot类型字段完全自定义。
Parser层:Schema到组件树的转换
Parser层负责将Schema解析为运行时组件树,处理联动条件和异步选项:
import { computed, reactive } from 'vue'
export function useFormParser(schema) {
const formData = reactive(
Object.fromEntries(
schema.fields.map(f => [f.field, f.default ?? null])
)
)
const visibleFields = computed(() => {
return schema.fields.filter(field => {
if (!field.visible) return true
return evaluateCondition(field.visible, formData)
})
})
function evaluateCondition(cond, data) {
const val = data[cond.field]
switch (cond.op) {
case 'eq': return val === cond.value
case 'neq': return val !== cond.value
case 'in': return cond.value.includes(val)
case 'gt': return val > cond.value
default: return true
}
}
return { formData, visibleFields }
}
visibleFields是computed属性,当formData中任何被引用的字段变化时自动重新计算,实现声明式的联动效果。
Renderer层:动态组件渲染实现
Renderer层基于visibleFields动态渲染组件,使用Vue3的component :is模式:
<template>
<el-form :model="formData" :rules="formRules" ref="formRef">
<el-row :gutter="layoutGap">
<template v-for="f in visibleFields" :key="f.field">
<el-col :span="24 / columns">
<el-form-item :label="f.label" :prop="f.field">
<component
:is="componentMap[f.type]"
v-model="formData[f.field]"
v-bind="f.props"
:options="resolveOptions(f)"
/>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
FormSlot是一个特殊组件类型,允许在JSON配置的表单中插入完全自定义的Vue3组件,用于处理复杂交互(如地址级联选择器、富文本编辑器)。
运行时校验与异步校验器
表单校验规则从Schema的rules字段提取,支持同步和异步校验:
function buildValidationRules(field) {
const rules = []
if (field.required) {
rules.push({ required: true, message: field.label + '不能为空' })
}
field.rules?.forEach(rule => {
if (rule.type === 'async') {
rules.push({
asyncValidator: async (_, value) => {
const valid = await rule.validator(value)
if (!valid) throw new Error(rule.message)
},
trigger: 'blur'
})
} else {
rules.push({ ...rule, trigger: 'blur' })
}
})
return rules
}
异步校验器适用于用户名唯一性检查、手机号格式验证等需要后端接口配合的场景。async-validator库会自动处理并发请求的去重和取消,避免频繁输入触发大量冗余请求。
动态Schema加载与缓存策略
当Schema由后端接口下发时,需要处理加载状态、缓存和版本更新:
const schemaCache = new Map()
const CACHE_TTL = 5 * 60 * 1000
async function loadSchema(formKey) {
const cached = schemaCache.get(formKey)
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.schema
}
const res = await fetch('/api/form-schema/' + formKey)
const schema = await res.json()
schemaCache.set(formKey, { schema, timestamp: Date.now() })
return schema
}
Schema变更后通过版本号或ETag机制让缓存失效。这种架构下,产品经理调整表单字段只需修改后台配置,无需前端发版,表单变更的交付周期从天级缩短到分钟级。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-dong-tai-biao-dan-xuan-ran-yin-qing-shi-xian-fang-an/