Vue 学习笔记-表单最佳实践(封装 + 校验 + 动态表单)
·
📝 作者:一名 Vue 的学习者
🕒 记录时间:2025年12月
💡 目标:熟悉表单组件的使用,实现如下效果:

在后台系统中,表单几乎是最核心的交互形式:新增、编辑、查询、配置……
如果每个页面都从头写一套,不仅重复,而且难维护。所以我在学习过程中,尝试总结并封装了一套通用表单方案。
本篇记录我对 Vue3 + Element Plus 表单的实践与思考。
一、常见表单需求
实际开发中,表单通常需要支持:
- 多种控件:input / select / radio / checkbox / date 等
- 表单校验(必填、长度、格式)
- 新增 / 编辑复用同一套表单
- 支持重置、回显数据
- 动态表单项(不同页面字段不同)
- 与弹窗 Dialog 组合使用
目标:一套 BaseForm,多个页面复用。
二、设计思路
- 基于
el-form二次封装 - 表单项用配置驱动(JSON 描述)
- 内部自动渲染不同控件
- 暴露方法:
validate / reset / getData / setData - 父组件只关心:字段配置 + 数据 + 提交逻辑
三、表单项配置示例
// utils/formSchemas.js
export const userFormSchemas = [
{
prop: "username",
label: "用户名",
type: "input",
placeholder: "请输入用户名",
rules: [{ required: true, message: "用户名不能为空", trigger: "blur" }]
},
{
prop: "password",
label: "密码",
type: "password",
placeholder: "请输入密码",
rules: [{ required: true, message: "密码不能为空", trigger: "blur" }]
},
{
prop: "role",
label: "角色",
type: "select",
options: [
{ label: "管理员", value: "admin" },
{ label: "普通用户", value: "user" }
],
rules: [{ required: true, message: "请选择角色", trigger: "change" }]
},
{
prop: "status",
label: "状态",
type: "radio",
options: [
{ label: "启用", value: 1 },
{ label: "禁用", value: 0 }
],
default: 1
}
];
四、BaseForm.vue 封装实现
src/components/base/BaseForm.vue
<template>
<el-form
ref="formRef"
:model="formModel"
:rules="rules"
label-width="100px"
>
<el-row :gutter="20">
<el-col
v-for="item in schemas"
:key="item.prop"
:span="item.span || 24"
>
<el-form-item :label="item.label" :prop="item.prop">
<!-- 输入框 -->
<el-input
v-if="item.type === 'input'"
v-model="formModel[item.prop]"
:placeholder="item.placeholder"
/>
<el-input
v-else-if="item.type === 'password'"
v-model="formModel[item.prop]"
show-password
:placeholder="item.placeholder"
/>
<!-- 下拉框 -->
<el-select
v-else-if="item.type === 'select'"
v-model="formModel[item.prop]"
:placeholder="item.placeholder"
style="width: 100%"
>
<el-option
v-for="opt in item.options || []"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="item.type === 'radio'"
v-model="formModel[item.prop]"
>
<el-radio
v-for="opt in item.options || []"
:key="opt.value"
:label="opt.value"
>
{{ opt.label }}
</el-radio>
</el-radio-group>
<!-- 插槽扩展 -->
<slot
v-else
:name="item.prop"
:model="formModel"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 操作区 -->
<div class="form-actions">
<el-button type="primary" @click="handleSubmit">提交</el-button>
<el-button @click="handleReset">重置</el-button>
</div>
</el-form>
</template>
<script setup>
import { ref, reactive, watch, defineExpose } from "vue";
const props = defineProps({
schemas: { type: Array, default: () => [] },
modelValue: { type: Object, default: () => ({}) }
});
const emits = defineEmits(["update:modelValue", "submit"]);
const formRef = ref(null);
const formModel = reactive({});
const rules = reactive({});
// 初始化表单
const initForm = () => {
props.schemas.forEach(item => {
formModel[item.prop] =
props.modelValue[item.prop] ??
item.default ??
"";
if (item.rules) rules[item.prop] = item.rules;
});
};
watch(
() => props.modelValue,
() => initForm(),
{ immediate: true, deep: true }
);
// 提交
const handleSubmit = () => {
formRef.value.validate(valid => {
if (valid) {
emits("submit", { ...formModel });
}
});
};
// 重置
const handleReset = () => {
formRef.value.resetFields();
};
// 暴露方法
defineExpose({
validate: () => formRef.value.validate(),
reset: handleReset,
getData: () => ({ ...formModel }),
setData: (data) => {
Object.assign(formModel, data);
}
});
</script>
<style scoped>
.form-actions {
text-align: right;
margin-top: 20px;
}
</style>
五、页面中使用表单
<template>
<el-card>
<BaseForm
ref="formRef"
:schemas="schemas"
:model-value="formData"
@submit="handleSubmit"
/>
</el-card>
</template>
<script setup>
import { ref } from "vue";
import BaseForm from "@/components/base/BaseForm.vue";
import { userFormSchemas } from "@/utils/formSchemas";
const formRef = ref(null);
const schemas = userFormSchemas;
const formData = ref({});
const handleSubmit = (data) => {
console.log("提交表单数据:", data);
// 调接口即可
};
</script>
六、与弹窗 Dialog 结合
<el-dialog v-model="visible" title="新增用户" width="500px">
<BaseForm
ref="formRef"
:schemas="schemas"
:model-value="formData"
@submit="submitForm"
/>
</el-dialog>
✔ 新增 / 编辑复用
✔ 打开弹窗时 setData 回显
✔ 关闭时 reset
七、小结
本篇我完成了:
✅ 基于 Element Plus 封装 BaseForm
✅ 表单项配置驱动渲染
✅ 支持校验、重置、回显
✅ 暴露方法给父组件调用
✅ 可与 Dialog / 页面自由组合
通过这种方式,后续新页面只需要:
👉 写 schemas + 提交逻辑,就能快速完成表单开发。
八、下一篇预告
👉 Vue 学习笔记(十四)|文件上传组件封装(单文件 / 多文件 / 进度条 / 预览)
更多推荐



所有评论(0)