配置化想法萌生
表格配置化主要能解决的问题就是 提高代码复用能力、提升开发效率,特别是需要开发多个大型表单系统的,配置化可以极大的提升效率,让你上班摸鱼不再是梦想!为了早点下班,我们接着往下看吧!
配置化思路其实就是对表单项进行了抽象,制定了一份协议去描述每个表单项,具体对象中的每个属性有什么用。
用配置化方式开发表单,完完全全就是为了高复用、可维护性,然后提升开发效率,解放生产力。
- 高复用:相似的业务逻辑进行统一处理,复用在相似领域的业务场景。如果说投放系统只需要接入一个渠道,那真的写
template
一把梭就完了。但事实上却不是这样的,当你接入了第一个facebook
,你发现后面还有tiktok
、巨量引擎
、广点通
等各种媒体渠道… - 可维护:实现配置代替开发。即使把配置抽离,交到非技术人员处,其根据协议一样能实现表单项的增删,完成业务。并不是把东西做出来就完事了。首先,渠道方会有新配置功能推出,这个是不可控的。其次,系统开发时并不是全字段接入,而是先接入业务方所需要的核心配置,所以后期会有很多接入新的字段需求。
设计协议
首先我们思考下我们的每个表单项目需要一些什么:
- type 类型。比如
input
、select
、radio
等等 - label 表单项的名称/描述
- formKey 字段名。我们提交数据到后段的字段名,比如
form.name
的'name'
- value 存放表单值。表单上
v-model
所绑定的值 - options 配置项。比如配置
multiple
、disabled
、是否显示
等等
举例如下:
config.js
export default [
{
type: 'el-input',
label: '活动名称',
formKey: 'name',
value: '', // 默认值为空字符串
options: {
vIf: [
// 表示:当 form.area === 'area1',才显示
{ relationKey: 'area', value: 'area1' }
]
}
},
{
type: 'el-select',
label: '活动区域',
formKey: 'area',
value: 'area1',
options: {
multiple: true
},
optionData: [ // 这里模拟去后端拉回数据
{ label: '区域1', value: 'area1' },
{ label: '区域2', value: 'area2' }
]
}
]
render函数
FormItemDemo.vue
<script>
export default {
name: "FormItemDemo",
props: {
itemConfig: Object // 接收配置,外部传入
},
computed: {
componentShow () {
const vIfArr = this.itemConfig?.options.vIf
if (!vIfArr) return true
const relationArr = this.config.filter(config => vIfArr.find(vIf => vIf.relationKey === config.formKey))
for (const relationItem of relationArr) {
const vIfItem = vIfArr.find(_ => _.relationKey === relationItem.formKey)
// 这里就是判断 联动的表单值 是否不满足 可以显示 的条件,不满足则不显示
if (relationItem.value !== vIfItem.value) return false
}
return true
}
},
render(createElement) {
//render 执行时判断是否展示,不展示的话返回空即可
if(this.componentShow) return
return createElement('el-form-item', {
props: {
label: this.itemConfig.label // 表单项的label
}
}, [
// 表单组件
createElement(this.itemConfig.type, {
props: {
value: this.itemConfig.value // 这里是自己实现一个 v-model
},
on: {
change: (nVal) => { // 这里是自己实现一个 v-model
this.itemConfig.value = nVal
}
}
}, this.itemConfig.optionData && this.itemConfig.optionData.map(option => {
// 这里只是 本demo 处理 el-select 的 option 数据,实际大家根据具体业务来实现即可
return createElement('el-option', { props: { label: option.label, value: option.value } })
}))
])
}
}
</script>
custom.vue
<template>
<div>
<el-form label-width="100px">
<FormItemDemo v-for="item in config" :item-config="item" />
</el-form>
</div>
</template>
<script>
import FormItemDemo from "./components/FormItemDemo.vue";
import config from "./config";
export default {
name: 'App',
components: { FormItemDemo },
data () {
return {
config
}
}
}
</script>
优化函数
采用这种方式对我们的 config 进行 非响应式 优化。其实整个 config 数据,我们只是需要保证 value 是响应式的即可,其他很多描述性数据都是大可不必的。那我们就把其他字段进行一个优化~
function optimize (array) {
return array.reduce((acc, cur) => {
for (const key of Object.keys(cur)) {
if (key === 'value') continue
// 将不是 value 的属性都进行非响应式优化
Object.defineProperty(cur, [key], { enumerable: false })
}
acc.push(cur)
return acc
}, [])
}