Vue3动态表单渲染引擎实现方案:JSON Schema驱动与运行时校验

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/

(0)
小编小编
上一篇 14小时前
下一篇 14小时前

相关推荐