🪜 写在前面
表单,是前端系统最基础但最复杂的模块之一。 它常常包含:
- 大量字段、交互逻辑、联动控制
- 多种校验规则、字段显示控制、动态字段生成
- 提交、校验、重置、草稿保存、只读模式等状态管理
- 表单配置平台化(低代码配置出页面)
本篇我们将一步步实现一套通用、动态、可扩展的表单系统,覆盖以下能力:
✅ 表单字段动态渲染
✅ 多类型字段支持(输入框/下拉/日期/开关/上传等)
✅ 校验规则可配置
✅ 字段联动控制(显示/禁用/值变化)
✅ 表单模式(编辑/只读)切换
✅ 支持后续平台化配置表单模板
🧱 一、表单渲染核心设计思想
用 JSON Schema + 渲染组件映射 实现动态表单:
ts
export interface FormSchema {
label: string
field: string
type: 'input' | 'select' | 'switch' | 'date' | 'upload'
required?: boolean
options?: Array<{ label: string; value: any }>
defaultValue?: any
rules?: Array<any>
showWhen?: (formModel) => boolean
disabledWhen?: (formModel) => boolean
}
📦 二、核心目录结构设计
bash
components/
├── DynamicForm.vue # 主表单组件
├── fields/ # 各类型组件
│ ├── InputField.vue
│ ├── SelectField.vue
│ ├── SwitchField.vue
│ └── ...
hooks/
├── useFormRender.ts # 渲染逻辑封装
├── useFormModel.ts # 状态管理封装
✅ 三、基础组件结构(DynamicForm)
vue
<!-- DynamicForm.vue -->
<template>
<Form ref="formRef" :model="formModel" :rules="rules">
<FormItem
v-for="schema in visibleSchemas"
:label="schema.label"
:prop="schema.field"
>
<component
:is="getFieldComponent(schema.type)"
v-model="formModel[schema.field]"
v-bind="getFieldProps(schema)"
:disabled="isDisabled(schema)"
/>
</FormItem>
</Form>
</template>
ts
// 渲染辅助逻辑
function getFieldComponent(type: string) {
return {
input: InputField,
select: SelectField,
switch: SwitchField,
}[type]
}
🧩 四、支持动态联动(showWhen、disabledWhen)
ts
const visibleSchemas = computed(() =>
props.schema.filter((schema) =>
schema.showWhen ? schema.showWhen(formModel.value) : true
)
)
function isDisabled(schema: FormSchema) {
return schema.disabledWhen ? schema.disabledWhen(formModel.value) : false
}
配置示例:
ts
{
field: 'username',
label: '用户名',
type: 'input',
required: true
},
{
field: 'adminCode',
label: '管理员编号',
type: 'input',
showWhen: (form) => form.role === 'admin'
}
🔐 五、校验规则支持
支持默认 + 自定义:
ts
{
field: 'email',
label: '邮箱',
type: 'input',
rules: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '格式不正确' },
],
}
表单验证封装:
ts
async function validate() {
return formRef.value.validate()
}
🧲 六、只读模式/编辑模式统一处理
方式1:组件接收 readonly
属性控制渲染
vue
<!-- InputField.vue -->
<template>
<span v-if="readonly">{{ modelValue }}</span>
<Input v-else v-model:value="modelValue" />
</template>
方式2:统一在 DynamicForm.vue
中处理
vue
<component
:is="getFieldComponent(schema.type)"
v-model="formModel[schema.field]"
:readonly="props.readonly"
:disabled="props.readonly || isDisabled(schema)"
/>
📦 七、支持插槽 & 自定义组件注册
vue
<FormItem v-if="schema.slot">
<slot :name="schema.slot" :form="formModel" />
</FormItem>
允许使用:
vue
<DynamicForm :schema="formSchema">
<template #customSlot="{ form }">
<Input v-model:value="form.customField" />
</template>
</DynamicForm>
🎯 八、平台化配置建议(支持后台配置)
后端返回:
json
[
{
"field": "username",
"label": "用户名",
"type": "input",
"required": true
},
{
"field": "role",
"label": "角色",
"type": "select",
"options": [
{ "label": "管理员", "value": "admin" },
{ "label": "普通用户", "value": "user" }
]
}
]
前端直接传入 schema,完成渲染。
🧠 九、性能优化建议
优化点 | 建议方案 |
---|---|
schema 频繁变动 | 使用 v-memo 或 shallowReactive |
只读表单渲染 | 使用只读组件(纯文本)+ 懒加载字段 |
联动过多 | 使用 computed 缓存联动状态 |
多类型组件动态渲染 | 使用缓存组件工厂 (defineAsyncComponent ) |
💡 十、组件封装建议(进阶)
类型 | 说明 |
---|---|
DynamicForm.vue |
渲染主组件 |
FormRender.ts |
生成组件映射表,支持自定义注册 |
useForm.ts |
暴露 validate/reset/setFields 等方法 |
useFormSchema.ts |
拆分 schema 过滤/联动/权限逻辑 |
FieldWrapper.vue |
可扩展插槽 + 错误提示包装器 |
🧠 总结一句话
表单系统的通用化设计,是支撑前端"配置化平台"、"低代码编辑器"的核心引擎。
下一篇我们进入高复用组件系统的核心场景:
👉 《表格系统架构:列配置、嵌套数据、复杂交互》