有麼得前端小夥伴跟我有同樣的煩惱,每次寫表單就煩。這東西吧沒有任何難度,就是純體力活,項目中若是用了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全局註冊了哦*