vue2 + element-ui,前端配置化表单封装(2024-06-14)

技术栈是 vue2 + element-ui,主要能解决的问题就是 提高代码复用能力、提升开发效率,特别是需要开发多个大型表单系统的,配置化可以极大的提升效率,让你上班摸鱼不再是梦想!为了早点下班,我们接着往下看吧!


一、场景介绍


1. 业务场景

如何定义「巨型」表单,这个因人而异。但如果只是一些:收货人信息、登陆、注册的这种比较简单的表单,那肯定算不上巨型,直接常规开发写模版就好了,没有必要为了配置化而配置化~

从笔者的理解出发,表单项非常多,比如笔者曾经负责的「投放系统」,随随便便提交时都会涉及几十甚至上百个字段,这样整个表单会有几十、上百个表单项组成,这就算得上是巨型表单了。

先给大家看看成品的其中的一小块截图~

别看到截图好像表单项也就那样,根据右栏数起来共40+个,但是这个只是初期版的,还有很多字段是没接进来的;而且很多表单项之间有联动、可增删,还有很多表单项是隐藏的

相信你很难想象,其实你只要进行简单的配置,就能实现上图的界面。比如下图的 js对象 就是上图的其中几个表单项的配置:

大家已经不难看出,配置化思路其实就是对表单项进行了抽象,制定了一份协议去描述每个表单项。具体对象中的每个属性有什么用,这个笔者稍后讲自己的设计思路时再详细介绍~

这时候你一定会有疑问,为什么要抽象、为什么配置化的方案更好,我们接着往下看~


2. 配置化想法萌生

高复用、好维护。是的,笔者用配置化方式开发表单,完完全全就是为了高复用、可维护性,然后提升开发效率,解放生产力。

  • 高复用:相似的业务逻辑进行统一处理,复用在相似领域的业务场景。如果说投放系统只需要接入一个渠道,那真的写 template 一把梭就完了。但事实上却不是这样的,当你接入了第一个 facebook ,你发现后面还有 tiktok 、 巨量引擎 、 广点通 等各种媒体渠道...

  • 可维护:实现配置代替开发。即使把配置抽离,交到非技术人员处,其根据协议一样能实现表单项的增删,完成业务。并不是把东西做出来就完事了。首先,渠道方会有新配置功能推出,这个是不可控的。其次,系统开发时并不是全字段接入,而是先接入业务方所需要的核心配置,所以后期会有很多接入新的字段需求。

接下来举两个例子来说说,高复用、好维护体现在哪里

  1. 表单1。代码如下:

<el-form ref="form" :model="form"> <!-- 当活动区域的值为 “area1” 时, 活动名称才展示 --><el-form-item label="活动名称" v-if="form.area === 'area1'"><el-input v-model="form.name"></el-input> </el-form-item> <el-form-item label="活动区域"><el-select v-model="form.area" multiple>...</el-select> </el-form-item> 
</el-form>  
  1. 表单2。虽然跟 表单1 很相似,但又存在不同。比如 表单2 的活动区域不叫 “活动区域” ,且 表单项 之间的联动关系有所不同,我们接着使用 copy大法 来做,代码如下:

<el-form ref="form" :model="form"><el-form-item label="活动名称"><el-input v-model="form.name"></el-input> </el-form-item> <!-- label变成 “活动2”,且需要填写name后才能操作,且是多选 --><el-form-item label="活动2"><el-select v-model="form.area" :disable="form.name" multiple >...</el-select> </el-form-item> 
</el-form>  

copy大法虽然好使,但是我们的复用能力基本就没了,所有功能都近乎是重新开发,这使得非常的被动。别看上面举例好像很轻松就能实现,笔者说过了,我们将要开发的是一个上百项表单项的系统,当模版的量堆积到一定程度时,你会想吐血。好不容写了上千行模版,以为完事了,结果再接一个新的媒体,又是从一个新的开始......并且,你要再写一个上千行的 template 和各种表单项之间的联动逻辑,也是很痛苦的...

提升复用能力 , 让复杂的表单 变得清晰好维护才是出发点。


二、设计 & 实现


1. 设计协议

首先我们思考下我们的每个表单项目需要一些什么:

  1. type 类型。比如 input 、 select 、 radio 等等

  2. label 表单项的名称/描述。

  3. formKey 字段名。我们提交数据到后段的字段名,比如 form.name 的 'name'

  4. value 存放表单值。表单上 v-model 所绑定的值

  5. options 配置项。比如配置 multiple 、 disabled 、 是否显示 等等

好了,有了以上这些点,我们试着把案例中的 表单1 用协议表达出来:

<el-form-item label="活动名称" v-if="form.area === 'area1'"><el-input v-model="form.name"></el-input> 
</el-form-item> 
<el-form-item label="活动区域"><el-select v-model="form.area">...</el-select> 
</el-form-item>

我们可以用协议这样去描述它

[{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}}
]

是不是有点内意思了?如果把 开发巨型表单系统 转换成 编写JSON ,是不是很爽?


2. 实现渲染器

配置是有了,但是怎么把配置转换成我们真实的表单呢?如果直接开干,我想大部分可能会先这样下手,比如:

<template><el-form-item :label="props.label"><el-input v-if="props.type === 'el-input' && ...业务联动逻辑" :disabled="props.disabled"v-model="props.value".../><el-select v-if="props.type === 'el-select' && ...业务联动逻辑" :disabled="props.disabled"multiple="props.multiple"v-model="props.value"...>...</el-select></el-form-item>
</template>

好了,大家观察一下上面的 template 中,有没发现很多冗余的代码。如果我们需要给组件传入 props 比如例子中的 disabled 、 multiple ;控制 v-if 等等。。我们有多少个组件,这些重复的代码就要写多少次。如果以后有需要给所有组件传多一个 props,我们就要编辑n次~记住!我们配置化就是要提高效率的,所以这样是不行的~

在此,笔者就建议编写 render函数render函数 的场景 & 对应的好处,大家可以看看 官方文档[1] 对其的讲解~

  • 这里不会深入介绍 render函数 ,如果还不知道的,大家只需要记住:Vue 只认 render函数,平时我们 .vue文件 写的 template ,经过编译之后就是 render函数

  • render函数 作用就是返回一个 vNode。我们 vue2 初始化项目时写的:render (h) => h(App)是不是就似曾相识了呢?

都说 React 写 jsx 比 Vue 写 template 更好写逻辑,那我们也用 render函数 ,好写逻辑~ 😝 (当然,如果你对render函数不是特别熟悉,那么写template也是可以的)

接下来,我们看看,如何通过render函数,把我们的表单项做出来,以上述案例其中一个为例子:

<el-form-item label="活动名称"><el-input v-model="form.name"></el-input> 
</el-form-item>

这一段要怎么通过render函数表述出来?根据官方文档,我们理清三个参数是什么就可以了:

createElement('div', // {String | Object | Function},一个 HTML 标签名、组件选项对象,或者...{}, // 一个与模板中 attribute 对应的数据对象[] // {String | Array},可以理解成时 children 节点
)

接着,我们直接开干:

<script>
export default {name: "FormItemDemo",render(createElement) {return createElement('el-form-item', {props: {label: '活动名称'}}, [createElement('el-input') // input组件])}
}
</script>

在 App组件 中引用这个 FormItemDemo组件,代码如下

<template><div><el-form label-width="100px"><FormItemDemo /></el-form></div>
</template><script>
import FormItemDemo from "./components/FormItemDemo.vue";export default {name: 'App',components: { FormItemDemo }
}
</script>

这时候,页面上就出现了我们的 input表单项 了

初始工作已经做完了,接下来的就是让我们把 render函数 的一些动态数据用变量代替,跟我们的 配置config 结合起来。

❗️❗️❗️注意,render函数很灵活,第一个参数可以是字符串、组件对象、function。大家不要被demo所局限,很多场景是需要我们定制一些组件,然后应用到我们的配置化中,而不是直接使用element-ui的原生组件。笔者项目中用的组件都是经过业务场景封装后export出来的组件对象,再配置到type中,最后render函数接收的是一个组件对象,具备业务能力的组件。因此,我们可以在自己实现的组件中定制自己的业务逻辑


3. render函数 & 配置数据

要说 render函数 也不是真的完美,毕竟要自己去实现譬如 v-if 、 v-model 这种指令,但是没问题,它带给我的便利给大,所以我能接受。

正式演示配置化的实现时,笔者先声明一点:这里的只是 demo 级别的,具体实战到项目要根据业务场景。笔者做业务时,是对 select 、 cascader 等组件都封装了一层。因为很多时候我们的下拉数据要去后端拿,封装后组件可以通过传入的 params 和 urlPath 去获取数据。所以,大家更要关注思路,然后根据业务场景自己去思考、实现即可

首先配置数据如下:

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函数 改造后,变成这样

<script>
export default {name: "FormItemDemo",props: {itemConfig: Object // 接收配置,外部传入},render(createElement) {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-modelthis.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>

接下来我们在 app组件 中同时应用我们的 配置 + FormItemDemo 组件:

<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>

这时候我们看下页面长什么样?

ok!!!实现了,接下来,我们只需要根据业务需求不断丰富我们的 FormItemDemo 组件即可。这里,笔者会带着大家一起实现一个 联动显示隐藏 、 下拉框多选 的功能~相信看完后,你一定有醍醐灌顶的感受,然后就可以自己根据业务去实现需求了。


4.丰富组件能力,实现业务

我们先来看第一个需求:当活动区域的值为 “area1” 时, 活动名称才展示

分析一下这个需求,我们的 input组件 跟 select组件 联动,所以 input组件 要获取 select组件 的值,这时候,我们可以在 app组件 中,将整个 config 传入 FormItemDemo组件

再回看一下我们的配置,我们把显示隐藏的配置放在 options.vIf 中(这里笔者设计成了一个数组,因为碰到的业务经常存在一个表单会受到好几个表单值联动的),所以 FormItemDemo组件 需要用这个来判断是否执行本次 render 以此来实现 v-if。如图所示:

用了一个 computed 去实现这个需求。大家可以不用仔细深入,只要知道 componentShow 的作用就是,找到联动的 relationKey 的 config 中的 value 值,判断是否跟配置的一致。

computed: {componentShow () {const vIfArr = this.itemConfig?.options.vIfif (!vIfArr) return trueconst 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}
}

模拟实现 v-if,只需要把上述计算属性在 render 的开头进行判断即可

ok,直接看下结果!两个表单项之间的联动完成了

接下来的需求,大家自行思考下怎么实现即可。其实都是异曲同工的

控制 select 多选 、 单选添加 filter 属性 ...

好了,这样子,基本上就大功告成了,只要我们把 FormItemDemo 的业务逻辑都实现了。


三、配置静态化

细心的朋友可能已经发现了,我们上述实现配置化的时候,直接把整个 config 赋值给 data ,然后在 App组件 的 el-form 中 v-for 使用,那这样避免不了就会出现一些尴尬的事情,比如我们看下图:

没错,就如大家所见,所有的属性都带上了 getter 和 setter,这意味着,他们都被初始化成了响应式的。由于我们的业务是非常复杂的,所以当我们真的要用一个 config 去描述整个表单时,config 的规模远不止以上这么点,并且整个配置对象的层级可能还会比较深,如果这样的话就可能会有性能问题了。

熟悉 Vue2 的同学都知道,初始化的时候,会对 data 做一个深度遍历添加 get 、 set 变成响应时数据,并且在组件执行 render函数 时,会访问到这些对象的属性。一旦访问到,就会触发 data属性 的依赖收集动作,如果无脑多的属性时,这个 get方法 将被无脑执行。

有深入看过 Vue2 源码的同学,对 __ob__ 这个属性一定不陌生,上面截图也有这个属性,但是大家发现没,这个 ob属性 却没有对应的 get 、set 。让我们打开源码,看看 尤大 做了什么?

首先,在进行响应式处理之前,调用了一个 def 的方法,这里 第四个参数 是没传的

看看 def 的具体实现,其实就是重新定义这个对象的属性。由于没传 enumerable,所以此时 __ob__的 enumerable 为 false

这样有什么用?一句话概括就是无法遍历到这个属性,后续响应式初始化时也会跳过这个属性。不清楚的伙伴可以看看笔者写的一个 demo 来加深理解:

没错,我们这里也是采用同样的方式对我们的 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}, [])
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/853487.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(Java微服务项目实战)dtpay聚合支付系统对账管理模块系统设计

1 聚合支付系统对账流程 dtpay聚合支付系统对账模块主要涵盖商户侧对账和渠道侧对账、平台侧对账&#xff0c;本文主要分析渠道侧对账。dtpay聚合支付系统通过支付渠道微信、支付宝等产生的支付退款交易数据需要和平台侧产生的数据进行交易数据比对。接下来我们具体分析对账流…

一款不写代码的开源爬虫工具!!【送源码】

爬虫&#xff0c;也被称为网络爬虫或网络蜘蛛&#xff0c;是一种自动化的网络机器人&#xff0c;其主要功能是按照一定的规则&#xff0c;自动浏览互联网并从网页中提取信息。 作为一个开发人员&#xff0c;相信大家都尝试过写一些爬虫&#xff0c;合理的利用一些爬虫工具&…

金融行业的等保测评要求

金融行业的等保&#xff08;网络安全等级保护&#xff09;测评要求是确保金融机构的信息系统达到一定的安全保护水平&#xff0c;以保护客户信息和金融交易的安全。等保测评在金融行业中的具体要求和流程主要包括以下几个方面&#xff1a; 等保级别 金融行业信息系统依据其重…

深入理解并打败C语言难关之一————指针(4)

前言&#xff1a; 我们在前面的几讲中已经讲了指针的很多内容了&#xff0c;现在我们开始层层递进&#xff0c;要探寻更多的指针喽&#xff0c;不多废话了&#xff0c;直接进入正题&#xff0c;开始今天的指针之旅喽&#xff01; 目录&#xff1a; 1.字符指针变量 1.1常量字符…

MEGALODON:突破传统,实现高效无限上下文长度的大规模语言模型预训练和推理

在人工智能领域&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;中&#xff0c;大模型&#xff08;LLMs&#xff09;的预训练和推理效率一直是研究的热点。最近&#xff0c;一项突破性的研究提出了一种新型神经网络架构——MEGALODON&#xff0c;旨在解决传统Tran…

阿里新发布的UniAnimate现高效人像动画生成;在ComfyUI中使用Stable 3模型;音频版的gpt2o;将 PDF 文档转换为音频播客

✨ 1: UniAnimate 阿里新发布的UniAnimate通过统一的视频扩散模型&#xff0c;实现高效人像动画生成&#xff0c;支持长视频生成 UniAnimate 是一种专注于一致性人像动画生成的统一视频扩散模型。该模型通过映射参考图像、姿势指导和噪声视频到一个共同特征空间&#xff0c;实…

ZED双目相机环境配置

官方资料&#xff1a;stereolabs/zed-python-api: Python API for the ZED SDK (github.com) 1&#xff0c;配置ZED相机环境 1.安装CUDA 查看电脑是否安装CUDA&#xff0c;安装过程可参考以下博文&#xff1a; 如何选择匹配的CUDA版本&#xff1a;https://blog.csdn.net/iam…

MoCo v3(ICCV 2021)

paper&#xff1a;An Empirical Study of Training Self-Supervised Vision Transformers official implementation&#xff1a;https://github.com/facebookresearch/moco-v3 出发点 本文并没有提出一种新的方法&#xff0c;而是对计算机视觉领域最近进展中的一个重要且基础…

沃尔玛自养号测评:优势与技术要求解析

沃尔玛自养号测评是一种卖家在沃尔玛平台上提升店铺权重和排名的营销手段。传统运营策略的局限性日益显现&#xff0c;如营销手段单一、难以应对市场竞争等。因此&#xff0c;许多卖家为了提升店铺权重和排名&#xff0c;选择了自养号测评这一技术手段。 以下是对沃尔玛自养号…

关于BERT和embedding

embedding到一个低维向量&#xff0c;但是需要回到onehot高维表示&#xff0c;所以大部分填词游戏最后都需要加上一个MLP接头。 word2vec如此简单的结构&#xff0c;学习到的是embedding 基于计数的统计方法和word2vec融合就形成了glove词嵌入模型 总结&#xff1a;通过各种…

消费者消费数据时报错:INVALID_REPLICATION_FACTOR

今天部署了kafka集群&#xff0c;三台服务器&#xff0c;启动后&#xff0c;生产者发送数据&#xff0c;消费者接收数据的时候报错&#xff0c;INVALID_REPLICATION_FACTOR。 查了很多资料&#xff0c;说是要改kafka下config目录的server.properties,可能是副本数太小&#xff…

【计算机视觉】人脸算法之图像处理基础知识(四)

图像的几何变换 图像的几何变换是指在不改变图像内容的前提下对图像的像素进行空间几何变换。主要包括图像的平移变换、镜像变换、缩放和旋转等。 1.插值算法 插值通常用来放缩图像大小&#xff0c;在图像处理中常见的插值算法有最邻近插值法、双线性插值法、二次立方、三次…

sap怎么批量给信息记录打上删除标识

1.MEMASSIN-----事务代码 2.选择完成字段 3.根据条件查询需要冻结的信息记录 4.输入查询条件 5.全部勾选完成标识&#xff0c;点击保存&#xff0c;即可冻结完成

盘点国内外免费AI视频工具,助你先人一步拥抱AI

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 6月13日&#xff0c;Luma AI 在 X 平台&#xff08;原 Twitter&#xff09;宣布其视频生成模型 Dream Machine 开放测试&#xff0c;并提供免费试用&#xff0c;这在海外 AI 圈掀起了一…

配置中心理论学习

配置中心是一种用于集中管理应用程序配置信息的系统或服务。在微服务架构中&#xff0c;由于服务数量众多且可能分布在不同的环境中&#xff0c;配置中心的作用尤为突出。它允许开发者将配置信息从应用程序代码中分离出来&#xff0c;集中存储和管理&#xff0c;从而提高配置的…

买什么样的护眼大路灯比较好?五款专业级别的护眼灯推荐

在这个生活节奏的加快和科技的进步的时代&#xff0c;许多家长越来越关注生活质量以及身心健康问题。其中孩子的眼睛视力健康也逐渐引起了家长们的注意。 现在的孩子从早上睁开眼就开始学习&#xff0c;有时候还需要使用到电子产品辅助学习&#xff0c;晚上的写作业的情况更是…

【perl】基本语法 /备忘录/

分享 perl 语言学习资源 Perl 教程|极客教程 (geek-docs.com) Perl [zh] (runebook.dev) Perl 运算符 | 菜鸟教程 (runoob.com) Perl Documentation - Perldoc Browser Search the CPAN - metacpan.org 当然还有一些经典书籍&#xff0c;不再列举。 1、数字 1.1、数字表…

判断QT程序是否重复运行

打开exe&#xff0c;再次打开进行提示。 main.cpp添加&#xff1a; #include "QtFilePreview.h" #include <QtWidgets/QApplication> #include <windows.h> #include <qmessagebox.h> #pragma execution_character_set("utf-8")bool Ch…

免费个人站 独立站 wordpress 自建网站

制作免费网站 | 免费网站构建器 | WordPress.com https://bioinformatics7.wordpress.com WordPress.com

【总线】设计fpga系统时,为什么要使用总线?

目录 为什么用总线 为什么选择AMBA 总结 系列文章 【总线】AMBA总线架构的发展历程-CSDN博客 【总线】设计fpga系统时&#xff0c;为什么要使用总线&#xff1f;-CSDN博客 【总线】AMBA总线家族的明星成员&#xff1a;AXI协议简介-CSDN博客 为什么用总线 在FPGA系统设计…