有么得前端小伙伴跟我有一样的懊恼,每次写表单就烦。这货色吧没有任何难度,就是纯体力活,我的项目中如果用了 UI 库,那根本每次就是照着官网实例复制粘贴。假如我的项目是用的 vue + elementUI,那常常会呈现这种代码
<template>
<div>
<el-input
v-model="data1"
placeholder="请输出内容"
@change="onchange" />
<el-input
v-model="data2"
type="textarea"
placeholder="请输出内容"
@change="onchange"/>
<el-switch
v-model="data3"
@change="onchange"/>
<el-slider
v-model="data4"
@change="onchange"/>
</div>
</template>
data(){
return{
data1:0,
data2:1,
data3:2,
data4:3,
}
}
methods:{onchange(value){........}
}
我这还是写的成心简略一些,如果属性和事件更多那这个页面将来就很有可能倒退成咱们作为程序眼最怕的事件。
老大:“小陈,XXX 到职了,你接手一下 XXX 文件,不难就是有点乱,也就一万多行吧”
我心田:“尼玛,明明几行代码加一个配置文件就搞定的事件,你可摇了我吧”
开始重构之路
(本文以 vue+elementUI 为例子,次要的是思维而不是代码)
- 剖析代码,找出异同
相同点:elementUI 中每所有表单元素最外层都被<form>
包裹,每一项都被<form-item>
包裹,每个具体的表单元素都是<el-XXX>
模式的,每个元素都能够通过value
或者v-model
来绑定数据。都具备一些公共的属性和事件,比方 change。除了 select,别的元素都能够用一行写完。
不同点:每个元素都有个性化的属性与办法,比方<el-input>
反对 type 属性,<el-slider>
反对 max,min 属性。 -
设计思路,代码实现
用一个数组对象来示意表单,每一项是一个 object,外面蕴含了表单标签所有用到的属性,标签名 tag、绑定字段 model、题目 label。
用一个对象来示意值,键值就是表单数组中申明过的那些。// 表单数组 let formList = [ { tag: 'input', model: 'name', label: '姓名' }, { tag: 'input-number', model: 'age', label: '年龄' }, { tag: 'switch', model: 'working', label: '是否退职' }, ] // 表单值 let formData = { name: '小陈', age: 26, working: false, }
数据的输入输出确定后就须要确定实现形式,能够写成一个 vue 组件,组件承受两个 props:[formList,formData],之后对 formList 进行遍历,应用动静组件
<component>
来依据 tag 渲染出对应对标签,v-mode
传入的 model 字段。再将 label 传给<form-item>
,<template> <el-form> <el-form-item v-for="(item,index) in formList" :key="index" :label="item.label" > <component v-model="formData[item.model]" :is="`el-${item.tag}`" /> </el-form-item> </form> </template>
- 解决差别,功败垂成
之前剖析得出,最大的差别就是每个元素都用很多个性化的属性与事件,并且除了表单元素,<el-form>
`<el-form-item>` 也有很多属性与事件,如果这些全都通过 prop 申明,那可太多了,并且还不好辨别谁是谁的。所以须要用到了 vue 的 $attrs 和 $listeners,
通过 $attrs 和 $listeners 批量绑定属性与事件,同时批改 formList 数据格式为
let formList = [
{
tag: 'input',
label: 'name',
model: 'name',
attrs: {
// 个性化属性
type: 'textarea'
},
listeners: {
// 个性化事件
change:(value)=>console.log(value,'change'),
blur:(value)=>console.log(value,'blur')
}
}
]
同时还要记得对 select 标签独自解决一下,最终代码为
```
<template>
<el-form v-bind="$attrs">
<template v-for="(item, index) in formList">
<el-form-item
:key="index"
v-bind="item.formItem"
v-if="item.tag ==='select'":label="item.label"
>
<el-select v-model="formData[item.model]" v-on="item.listeners" v-bind="item.attrs">
<el-option
:value="option.value"
:label="option.label"
:key="idx"
v-for="(option,idx) in item.options"
></el-option>
</el-select>
</el-form-item>
<el-form-item v-else :key="index" v-bind="item.formItem" :label="item.label">
<component
v-model="formData[item.model]"
v-on="item.listeners"
v-bind="item.attrs"
:is="`el-${item.tag}`"
></component>
</el-form-item>
</template>
</el-form>
</template>
<script>
export default {
props: {
formList: {
type: Array,
required: true,
},
formData: {
type: Object,
default: () => ({}),
},
},
};
</script>
<style>
</style>
```
* 留神:本例子默认 elementUI 全局注册了哦 *
- 成果
成果
代码