有么得前端小伙伴跟我有同样的烦恼,每次写表单就烦。这东西吧没有任何难度,就是纯体力活,项目中若是用了UI库,那基本每次就是照着官网实例复制粘贴。假设项目是用的vue + elementUI,那常常会出现这种代码javascript
<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文件,不难就是有点乱,也就一万多行吧”
我心里:“尼玛,明明几行代码加一个配置文件就搞定的事情,你可摇了我吧”html
开始重构之路
(本文以vue+elementUI为例子,主要的是思想而不是代码)前端
<form>
包裹,每一项都被<form-item>
包裹,每一个具体的表单元素都是<el-XXX>
形式的,每一个元素均可以经过value
或者v-model
来绑定数据。都具备一些公共的属性和事件,好比change。除了select,别的元素均可以用一行写完。<el-input>
支持type属性,<el-slider>
支持max,min属性。设计思路,代码实现
用一个数组对象来表示表单,每一项是一个object,里面包含了表单标签全部用到的属性,标签名tag、绑定字段model、标题label。
用一个对象来表示值,键值就是表单数组中声明过的那些。vue
// 表单数组 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>
,java
<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数据格式为git
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全局注册了哦*