vue 封装一个高质量的表单通用组件
正文
我们都知道表单组件应该是后台管理系统中用得最多的组件,我们不可能每个业务都写一次表单,然后每一次修改都去各自业务中大幅修改,这样就可能导致代码重复率太高了,工作效率频频降低,所以我们需要封装起来,这样我们就可以复用,大大减少项目体积,方便项目的后期维护,提高前端娃的工作效率。由于我们使用表单都是直接使用UI组件库的组件,所以我们需要做二次封装,那么问题来了,二次封装表单组件我们需要考虑什么?
基于Element-plus实现二次封装表单组件
- 特性复用:必须继承原有组件的所有特性。
- 命名规范:二次组件名必须见名知意,我们一般都是起一个公用名+原有组件名,比如lib-form。
- 接口简单:自定义暴露出来的接口越简单越好。
- 容易拓展:留有自定义插槽,让用户可以自己选择。
- 功能完善:具备更完善的功能如:表单验证、动态删减表单,集成第三方的插件(富文本)...
- 场景通用:具备多个场景使用,比如弹框嵌套表单、页面嵌套表单。
封装一个高质量的通用组件,上面是真的只是基操,话不多说,直接上实践手把手教你封装组件。
步骤1
继承原有组件的所有特性(这也是封装的核心)。先明确三个大方向:
表单固定属性,继承Form表单的所有属性、方法。
// 定义el-form的ref继承原有组件的form属性
export interface FormInstance {
registerLabelWidth(width: number, oldWidth: number): void,
deregisterLabelWidth(width: number): void,
autoLabelWidth: string | undefined,
emit: (evt: string, ...args: any[]) => void,
labelSuffix: string,
inline?: boolean,
model?: Record<string, unknown>,
size?: string,
showMessage?: boolean,
labelPosition?: string,
labelWidth?: string,
rules?: Record<string, unknown>,
statusIcon?: boolean,
hideRequiredAsterisk?: boolean,
disabled?: boolean,
validate: (callback?: Callback) => Promise<boolean>,
resetFields: () => void,
clearValidate: (props?: string | string[]) => void,
validateField: (props: string | string[], cb: ValidateFieldCallback) => void,
}
表单项固定属性,继承表单项的所有属性、方法。
// 表单每一项的配置选项
export interface FormOptions {
// 表单项显示的元素
type: '',// 定义表单项类型
value?: any, // 表单项的值
label?: string,// 表单项label
prop?: string,// 表单项的标识
rules?: RuleItem[],// 表单项的验证规则
placeholder?: string,// 表单项的占位符
attrs?: { // 按需定义不同表单类型属性
...
},
children?: FormOptions[],// 表单项的子元素,可能存在嵌套表单组件,如select
....// 适当扩展我们需要的属性,比如上传组件属性,行布局表单属性
}
由于Element-plus组件都是以el-为前缀,所以type取值只需要取el-后面部分作为值就行,比如el-input取 'input' 为值。
表单验证效果,继承组件原有的所有验证属性。由于Element-plus的验证都是使用 async-validator 这个插件的验证方法,直接复用插件源码路径async-validator/class="lazy" data-src/interface.ts文件下的所有代码:
// 核心代码:封装验证方式时的属性
export interface RuleItem {
type?: RuleType; // 验证种类
required?: boolean;// 是否必填
pattern?: RegExp | string;// 验证方式匹配
min?: number; // 表单项最小值
max?: number; // 表单项最大值
len?: number; // 表单项字符长度
trigger?: string | string[];// 验证触发方式
....
}
步骤2
实现一个完善的通用组件封装,通过对标签封装、接口 暴露、开发者传参等。明确表单类型,根据不同类型表单复用多种场景,不仅开发者用户拓展,而且,让开发者用最少代码就可以复用:
<!--表单框架:model就是传入的表单对象,rules就是传入的验证对象 -->
<el-form
v-if="model"
:validate-on-rule-change="false"
v-bind="$attrs"
:model="model"
:rules="rules"
ref="form"
>
... <!--表单项封装 -->
</el-form>
普通表单项封装,比如日期、输入等组件。
<template v-for="(item, index) in options" :key="index">
<el-form-item :label="item.label" :prop="item.prop">
<component
v-else
:is="`el-${item.type}`"
v-bind="item.attrs"
v-model="model[item?.prop!]"
>
</component>
</el-form-item>
</template>
嵌套表单项封装,比如下拉框,除了select组件还嵌套option组件。
<template v-for="(item, index) in options" :key="index">
<el-form-item
v-if="item.children && item.children.length"
:label="item.label"
:prop="item.prop"
>
<component
:is="`el-${item.type}`"
v-bind="item.attrs"
v-model="model[item?.prop!]"
>
<component
v-for="(child, i) in item.children"
:key="i"
:label="child.label"
:value="child.value"
:is="`el-${child.type}`"
>
</component>
</component>
</el-form-item>
</template>
富文本表单项封装
本文使用的是wangEditor。
<div id="editor" v-else-if="item.type === 'editor'"></div>
import E from 'wangeditor';
// 遍历传入的prop的options对象,初始化富文本
if (item.type === 'editor') {
// 初始化富文本
nextTick(() => {
if (document.getElementById('editor')) {
const editor = new E('#editor');
editor.config.placeholder = item.placeholder!;
editor.create();
// 初始化富文本的内容
editor.txt.html(item.value);
editor.config.onchange = (newHtml: string) => {
model.value[item.prop!] = newHtml;
};
edit.value = editor;
}
});
}
上传表单项封装
向开发者暴露上传的核心方法:预览、删除、上传成功等,同时允许开发者自定义上传信息以及渲染区域等。
<el-form-item :label="item.label" :prop="item.prop">
<!-- 上传表单 -->
<el-upload
v-if="item.type === 'upload'"
v-bind="item.uploadAttrs"
:on-preview="onPreview"
:on-remove="onRemove"
:on-success="onSuccess"
:on-error="onError"
:on-progress="onProgress"
:on-change="onChange"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
:http-request="httpRequest"
>
<slot name="uploadArea"></slot>
<slot name="uploadTip"></slot>
</el-upload>
</el-form-item>
同行多个表单布局封装
有时业务需要,一行可以定义多个表单,所以需要使用el-row,此时需要修改FormOptions属性接口,完善多个表单场景,cols是一个数组定义FormOptions数组,colOption是el-col组件的相关属性,然后重新复用嵌套表单的代码。
<template v-if="item.type === 'row'">
<el-row :gutter="item.rowGutter">
<el-col
v-for="(jtem, jndex) in item.cols"
v-bind="jtem.colOption"
:key="jndex"
>
<el-form-item :label="jtem.label" :prop="jtem.prop">
<component
:is="`el-${jtem.type}`"
v-bind="jtem.attrs"
v-model="model[jtem?.prop!]"
>
<template v-if="jtem.children && jtem.children.length">
<component
v-for="(child, i) in jtem.children"
:key="i"
:label="child.label"
:value="child.value"
:is="`el-${child.type}`"
>
</component>
</template>
</component>
</el-form-item>
</el-col>
</el-row>
</template>
- 自定义插槽:开发者可以根据需要,在封装的el-form中添加插槽,可以允许组件功能的拓展,我们可以根据自己需要进行封装,这里就不一一演示了。
提交取消按钮区域:这个最好可以实现插槽让开发者可以自定义。
<el-form-item>
<slot name="action" :form="form" :model="model"></slot>
</el-form-item>
步骤3
开发者的调用封装组件,通过配置不同表单类型的数组,然后调用lib-form封装组件实现业务代码复用。
组件的调用:根据业务需要,可以适当定义我们需要的组件属性以及必须要传的参数。
<lib-form
ref="form"
label-width="100px"
:options="options"
@on-change="handleChange"
@before-upload="handleBeforeUpload"
@on-preview="handlePreview"
@on-remove="handleRemove"
@before-remove="beforeRemove"
@on-success="handleSuccess"
@on-exceed="handleExceed"
>
<template #uploadArea>
<el-button size="small" type="primary">Click to upload</el-button>
</template>
<template #uploadTip>
<div style="color: #ccc; font-size: 12px">
jpg/png files with a size less than 500kb
</div>
</template>
<template #action="scope">
<el-button type="primary" @click="submitForm(scope)">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</template>
</lib-form>
表单项的配置数组:由于这配置数组比较长,所以一般可以单独抽离出来,不要写在vue文件中,这样可以提高代码的可读性。
// 这里以多行表单布局为例子
let options: FormOptions[] = [
{
type: 'row',
rowGutter: 20,
cols: [
{
type: 'input',
value: '',
label: '用户名',
prop: 'username',
placeholder: '请输入用户名',
rules: [
{
required: true,
message: '用户名不能为空',
trigger: 'blur',
},
{
min: 2,
max: 6,
message: '用户名在2-6位之间',
trigger: 'blur',
},
],
attrs: {
clearable: true,
},
colOption: {
offset: 0,
span: 12,
},
},
{
type: 'input',
value: '',
label: '用户名',
prop: 'username',
placeholder: '请输入用户名',
rules: [
{
required: true,
message: '用户名不能为空',
trigger: 'blur',
},
{
min: 2,
max: 6,
message: '用户名在2-6位之间',
trigger: 'blur',
},
],
attrs: {
clearable: true,
},
colOption: {
offset: 0,
span: 12,
},
},
],
},
]
总结
UI组件二次封装其实不难,只要我们能掌握基本语法,就可以根据业务场景去封装我们需要的组件,然后在慢慢去完善这个组件的功能。只要遵循这些原则:命名规范、组件通用、特性继承、场景通用、接口简单、兼容性强、功能完善。
以上就是vue 封装一个高质量的表单通用组件的详细内容,更多关于vue 封装表单通用组件的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341