有么得前端小伙伴跟我有一样的懊恼,每次写表单就烦。这货色吧没有任何难度,就是纯体力活,我的项目中如果用了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全局注册了哦*
- 成果
成果
代码