Vue规则表单组件的封装
业务背景
业务场景是这样的,目前有一个表单,表单中28个表单项,但这些表单项只有三种类型,一种是输入框,一种是选择框,还有一种是输入域。每一个label都是一个可点击a标签,此外在label的后面,紧跟着一个图标,鼠标悬浮图标上会有气泡信息,显示该表单项的更多的信息。
页面的效果是长这个样子的
表单是基于Element-ui来完成的,按照常规的写法是这样做的,
<el-form ref="form" label-position="top" :model="jobContentForm" label-width="80px" >
<p>
<a href="https://docs.gitlab.com/ee/ci/yaml/README.html#job-keywords" target="_blank" >官方字段解释</a>
</p>
<el-form-item label="任务名">
<el-input v-model="jobContentForm.name"></el-input>
</el-form-item>
<el-form-item label="镜像">
<el-input v-model="jobContentForm.image"></el-input>
</el-form-item>
<el-form-item label="服务">
<el-input v-model="jobContentForm.services"></el-input>
</el-form-item>
<el-form-item label="前置脚本">
<el-input type="textarea" v-model="jobContentForm.before_script"></el-input>
</el-form-item>
<el-form-item label="后置脚本">
<el-input type="textarea" v-model="jobContentForm.after_script"></el-input>
</el-form-item>
<el-form-item label="Runner tags">
<el-input v-model="jobContentForm.tags"></el-input>
</el-form-item>
<!-- 此处还需要重复18个 el-form-item 标签-->
</el-form>
这里的每一个el-form-item
都是一个表单项,一共有23个。
这种写法有个很大的缺点,那就是冗余代码太多,每一个项都要用el-form-item
来包裹,而且代码非常的多,要修改一个表单也非常不好找要修改的数据。
做程序员就要敬精益求精,如果程序让你感觉不爽,那就优化它。
优化思路
于是乎,一段封装组件的历程由此开始。
解决这种冗余代码的难题有一个很明显的思路,那就是数据驱动,将代码压缩到极致。表单项由数据配置,组件的渲染也由数据驱动。这样实现下来,只需要一个el-input ,
一个el-select
, 修改配置数据也变得非常简单。
说干就干。
提取数据。
提取数据是一个技术活,它要求你要将事物抽丝剥茧,抽象起来。现在让我们一步一步来分析一下。
对照着页面,我们很容易想到每一个输入框,每一个选择框 都是一个对象,
这个对象会有
- 唯一主键id,
- 表单项的label,
- 表单项的默认值 defaultValue
- 表单项的类型type,type为input时,表明当前表单项为el-input, 为select时表单项为el-select
- 点击label要到达的链接, link
- label的详细解释 des
- 表单项的 placeholder
- 如果表单项是select时,需要有待选项 options这样一个数组
那么一个表单项的数据大致就是这样的
formItem = {
id: 'image',
label: '镜像',
defaultValue: '',
type: 'input',
link: 'https://www.baidu.com/',
des: 'Docker 镜像名',
placeholder: '请输入',
options:[{label:1,value:1},{label:2,value:2}]
},
提取出一个表单项后,举一反三,我们就能很简单地列举出其他表单项的配置上数据。只是改一下值而已。
一个表单中有多个对象, 这多个对象就组成了一个数组。这个数组就是一个表单的全部配置数据。
经过抽象提取,一个表单的数据大致是这个样子的
export const globaleForm = [
{ id: 'image', label: '镜像', defaultValue: '', type: 'input', link: 'https://www.baidu.com/', des: '12445' , placeholder: '请输入'},
{ id: 'services', label: '服务', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
{ id: 'before_script', label: '前置脚本', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
{ id: 'after_script', label: '后置脚本', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
{ id: 'tags', label: 'Runner标签', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
{ id: 'cache', label: '缓存', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
{ id: 'artifacts', label: '制品', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
{ id: 'retry', label: '重试次数', defaultValue: '', type: 'select', link: '', des: '' , placeholder: '请输入',
options:[{label:1,value:1},{label:2,value:2},{label:3,value:3},]
},
{ id: 'timeout', label: '超时时间', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
{ id: 'interruptible', label: '是否中断', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
]
以上便是最终的配置数据。
提取组件
提取完表单的配置数据后,我们就要进行组件的封装,
由于是一个表单,这里肯定是少不了一个el-form
和 一个v-for
去遍历globaleForm
创建一个 CustForom.vue
的组件,将表单的配置参数globaleForm
传入。
内容如下
<template>
<el-form :ref="formRef" label-position="top" label-width="80px" :model="formValue">
<el-form-item :label="item.label" v-for="item in formItemArr" :key="item.id" >
<!--TODO-->
</el-form-item>
</el-form>
</template>
<script>
export default {
props: {
// 表单的配置数据
formItemArr: {
type: Array,
default: () => [],
},
}
};
</script>
v-for遍历的内部是组件的核心组件结构,由于我们的label是一个可以点击的,并且有tip气泡效果的,那么我们可以使用label的插槽来实现。
lebel这部分的代码就是这样子的,item是遍历的元素
<template slot="label">
<a class="label-link-info" target="_blank" :href="item.link">
<span class="label-text">{{item.label}}</span>
<el-tooltip v-if="item.des" class="item" effect="dark" :content="item.des" placement="top">
<i class="el-icon-info"></i>
</el-tooltip>
</a>
</template>
处理完label,下面就是输入框与选择框的逻辑判断了, 由于我们用属性type来表明当前的表单项是input 还是select,所有这里的逻辑很简单,
如果type是input,那么就渲染一个el-input, 如果是select没那么就渲染有一个el-select.,另外我们需要一个对象来存储用户输入的数据,命名为formValue, 并且需要将配置的表单数据中的默认值,赋值给formValue
表单输入区域的组件结构就变成这样了.分析到这一步 组件的代码就变成了以下这样子
<template>
<el-form :ref="formRef" label-position="top" label-width="80px" :model="formValue">
<el-form-item :label="item.label" v-for="item in formItemArr" :key="item.id" >
<!--label插槽的设置 -->
<template slot="label">
<a class="label-link-info" target="_blank" :href="item.link">
<span class="label-text">{{item.label}}</span>
<el-tooltip v-if="item.des" class="item" effect="dark" :content="item.des" placement="top">
<i class="el-icon-info"></i>
</el-tooltip>
</a>
</template>
<!--输入框 输入域的渲染-->
<el-input v-if="item.type === 'input'" :type="item.inputType" v-model="formValue[item.id]"></el-input>
<!--选择框的渲染-->
<el-select v-if="item.type === 'select'" v-model="formValue[item.id]" placeholder="请选择">
<el-option v-for="oitem in item.options" :key="oitem.value" :label="oitem.label" :value="oitem.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<script>
export default {
props: {
formItemArr: {
type: Array,
default: () => [],
},
},
data() {
return {
formValue: {},
};
},
watch: {
// 监听formItemArr,将formValue的值设置上
formItemArr: {
handler(v) {
if (!v.length) return;
v.forEach((x) => {
this.$set(this.formValue, x.id, x.defaultValue || "");
});
},
immediate: true,
},
},
};
</script>
到了这一步我们已经完成了大部分,配置好数据,调试以下。只要在编写一个方法,将表单的值对外暴露一下即可。
这样写
methods: {
getFormValue() {
return this.formValue
},
},
然后我们在组件调用的时候使用ref获取组件的实例,进而调取getFormValue()
方法,就是简单地在父组件里调用子组件的方法从而获取到自组件内部的data
数据.
写在最后
整个过程说复杂也不复杂,说简单也不简单,重要的是大家要掌握抽象组件,提取配置数据的思路。
注意,通过数据驱动编写的组件,扩展性并不是很好, 与不封装,单独调用底层的方式相比。如果你的表单,业务逻辑很复杂,并且还有很多动态元素。建议还是不用这种数据驱动的表单组件。
- 分享
- 举报
-
浏览量:7623次2020-12-07 16:50:17
-
浏览量:662次2023-11-24 15:35:13
-
浏览量:7539次2021-01-15 12:04:13
-
浏览量:10297次2020-12-07 23:36:10
-
浏览量:2693次2022-02-17 09:00:18
-
浏览量:2952次2020-12-12 09:53:13
-
浏览量:664次2024-01-03 12:58:35
-
浏览量:18366次2021-03-15 15:41:39
-
浏览量:2137次2020-01-04 13:43:04
-
浏览量:2851次2021-04-08 15:45:16
-
浏览量:1628次2021-12-07 09:52:19
-
浏览量:4291次2021-08-26 14:50:36
-
浏览量:712次2023-11-30 17:09:29
-
浏览量:33614次2021-02-02 16:24:27
-
浏览量:2499次2020-11-18 09:34:34
-
浏览量:18948次2021-03-22 17:45:29
-
浏览量:618次2023-07-28 10:19:40
-
浏览量:3238次2020-08-14 18:10:33
-
浏览量:1034次2023-07-26 14:12:28
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
这把我C
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明