首页 > 技术知识 > 正文

业务背景

业务场景是这样的,目前有一个表单,表单中28个表单项,但这些表单项只有三种类型,一种是输入框,一种是选择框,还有一种是输入域。每一个label都是一个可点击a标签,此外在label的后面,紧跟着一个图标,鼠标悬浮图标上会有气泡信息,显示该表单项的更多的信息。 页面的效果是长这个样子的

Vue规则表单组件的封装

表单是基于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数据.

写在最后

整个过程说复杂也不复杂,说简单也不简单,重要的是大家要掌握抽象组件,提取配置数据的思路。 注意,通过数据驱动编写的组件,扩展性并不是很好, 与不封装,单独调用底层的方式相比。如果你的表单,业务逻辑很复杂,并且还有很多动态元素。建议还是不用这种数据驱动的表单组件。

猜你喜欢