推荐词:
目前企业经常会遇到几个相似的业务场景重复开发,重新迭代的问题。前端也遇到了代码的可维护性、可重用性、开发效率等各种问题。本文针对相似度高的业务场景带来的代码冗余和重复工作量问题,针对vue2下的大表单,通过配置实现显著提高前端开发效率,是一个贴合业务场景的优质实践案例。

——MobTech浩瀚科技前端开发专家小千
本文从三个部分开始:场景介绍、设计实现和性能优化。当时作者的技术栈是vue2+element-ui,主要是为了提高代码复用能力和开发效率。特别是对于开发多个大型表单系统的需求,配置可以大大提高开发效率。快来学习吧!风景介绍
业务场景
如何定义一个“巨型”形态?从笔者的理解来说,比如“投递系统”在提交时会涉及几十个甚至上百个字段,这样整个表单就会由几十个或者上百个表单项组成,可以看作是一个巨型表单。
先给大家看几张成品截图:根据右边的列号,截图中有40+个表单项。这只是初始版本,包括很多没有计算的字段。表单项目是链接的,可以添加或删除,有些表单项目是隐藏的。很难想象,其实上面的界面可以通过简单的配置来实现。比如下图中的js对象就是上图中几个表单项的配置:不难看出,配置的思路其实就是把表单项抽象出来,通过制定协议来描述每个表单项。配置创意启动
作者通过配置开发表单的目的是为了提高开发效率,解放生产力。
复用度高:相似的业务逻辑统一处理,相似领域的业务场景复用。如果交付系统只需要连接到一个通道,只需编写模板。但事实上并非如此。当你访问第一个facebook时,你会发现有各种媒体渠道,如tiktok、庞大引擎和广点通。...
可维护性:实现配置而不是开发。即使把配置拆下来交给非技术人员,也可以根据协议增删表单项,完成业务。但是,当任务简单地实现时,它并没有结束。首先,渠道端随时推出新的配置功能,这是不可控的。其次,系统开发时不是全字段接入,而是先接入业务方需要的核心配置。随着业务的发展,以后会接入更多新的领域需求。
这里有两个高度重用和良好维护的例子:1表格。代码如下:
...
表格2。虽然和形式1类似,但是不一样。比如表单2的活动区域不叫“活动区域”,表单项之间的联动关系是不一样的。然后我们使用复制解决方案来实现它,代码如下:
...
虽然复制大法效果不错,但是也失去了重用性。所有功能几乎都是二次开发,这让开发者很被动。上面的案例看似很容易实现,但如果要开发一个有上百个表单项的系统,当模板数量积累到一定程度时,操作难度就会大大增加。
在你写了几千行模板之后,如果你收到一个新的媒体,那将是一个新的开始。而且,你还需要再写一千行模板和各种表单项之间的链接逻辑。因此,如何提高复用能力,如何使复杂的表单清晰易维护是作者的目标。设计和实施
设计协议
让我们首先考虑每个表单项目需要哪些信息:
类型。例如输入、选择、无线电等。
标签项目的名称/描述。
FormKey字段名称。我们将数据提交到最后的字段名,比如form.name的' name '
值存储表单值。由窗体上的v模型绑定的值。
选项配置项目。如配置多个,禁用,是否显示等。
通过以上信息,我们可以尝试用协议来表达案例中的形式1:...我们可以用这个协议来描述它:
[{type: 'el-input ',label:' activity name ',formKey: 'name ',value:' ',//默认值为空string options:{ vIf:[//means:当form.area = = = ' area1 ',only {relation key:' area ',value:' area 1'}]},{type:' El-select ',label:' active area ',formkey:' area ',value:' area 1 ',options: {multiple: true}}]把巨人表单系统的开发转换成写JSON不是更好吗?
实现渲染器
如何将配置转换成我们的真实形式?在大多数情况下,您可以先这样做:
...
我们看看上面的模板有没有很多多余的代码。如果我们需要给组件传入道具,比如例子中的disabled和multiple控制v-if等等。
只要我们有组件需求,我们就必须多次编写这些重复的代码。如果以后有必要给所有组件多发一个道具,我们要编辑n次。配置的最终目的是提高效率。这里,作者建议写渲染函数。渲染函数的场景相应的优点,可以在官方文档中看到对它的解释。
render函数返回一个vNode。vue2初始化项目时我们写的:render => h,是不是很熟悉?
都说React写jsx比Vue写template写逻辑好,所以我们也用render函数轻松写逻辑。接下来,让我们看看如何通过render函数实现表单项:如何通过render函数表达这一段?根据官方文件,我们整理出三个参数:
标签名然后开始:
使用以下代码在App组件中引用此FormItemDemo组件:
这时,我们的输入表单项出现在页面上:初步工作已经完成。接下来,渲染函数的一些动态数据被替换为变量,并与配置config相结合。值得注意的是,渲染功能是灵活的。第一个参数可以是字符串、组件对象和函数。不要被demo所限制。很多场景需要我们定制一些组件,然后应用到我们的配置中,而不是直接使用element-ui的原生组件。项目中使用的组件都是经过业务场景封装,然后配置成类型后的导出组件对象。最后,render函数接收一个组件对象,这是一个具有业务功能的组件。因此,我们可以在我们实现的组件中定制我们的业务逻辑。
呈现功能和配置数据
当然,render函数要实现v-if、v-model之类的指令还是比较困难的,但是它的便利性远大于它的缺点。
请注意,这只是一个演示级别的示例。在正式演示配置的实现时,需要根据业务场景进行调整。当作者将其应用于业务时,它封装了一层组件,如select和cascader。
大多数情况下,下拉数据需要从后端获取,封装的组件可以通过传入的params和urlPath获取数据。所以大家要多关注创意,然后根据业务场景去思考和实现。
首先,配置数据如下:导出默认[{type:' El-input ',label:' activity name ',formKey: 'name ',value:' ',//默认值为空string options:{ vIf:[//means:当form.area = = = ' area1 ',only {relation key:' area ',value:' area 1 ' } } },{type:' El-select ',label:' active area ',formkey:' area ',value:' area 1 ',options: {multiple: true},Optiondata: [//此处模拟后端拉取数据{label:' area 1 ',value:' area 1 '在我们修改了渲染函数之后,它变成了这样:
接下来,我们同时在app组件中应用我们的configuration+FormItemDemo组件:
让我们看看此时的页面是什么样子的。搞定了!接下来,我们只需要根据业务需求不断丰富我们的FormItemDemo组件。这里笔者就带大家一起来实现联动显示隐藏和下拉框多选的功能。相信你看完之后一定有一种醍醐灌顶的感觉。丰富组件能力,实现商业。
让我们先来看看第一个要求:
当活动区域的值为“area1”时,显示活动名称。
分析这个需求。我们的输入组件与选择组件相链接,因此输入组件需要获取选择组件的值。此时,我们可以将整个配置转移到app组件中的FormItemDemo组件中。
回头看看我们的配置,我们把隐藏的配置放在options.vIf中,所以FormItemDemo组件需要用这个来决定是否执行这个渲染来实现v-if。如图所示:
作者用计算机实现了这一要求。不必仔细深究,只要知道componentShow的作用就是在链接的relationKey的config中查找value值,判断是否与配置一致即可。computed:{ componentShow { const vIfArr = this . item config . options . Vif if return true const relationArr = this . config . filter)For { const vIfItem = vIfArr . find//这里是判断链接的表单值是否不满足可以显示的条件。否则,如果return false} return true }}将不会显示}}
模拟v-if,只需要在渲染开始时判断上述计算属性即可:
直接看结果,两个表单项联动完成!接下来的需求,你可以自己想想怎么实现,其实方式也是一样的。选择控制多重选择和单一选择。
添加过滤器属性...
基本上以上都做到了。当我们已经实现了FormItemDemo的业务逻辑,无论如何开发N个表单系统,只需要配置就可以轻松实现。然而,一个优秀的前端怎么可能止步于此?
静态配置
细心的朋友可能发现了,我们在实现上面的配置时,直接把整个config赋给data,然后用v-for在App组件的el-form中使用。那么如果这是不可避免的,就会发生一些尴尬的事情,比如下图:
如您所见,所有属性都有getter和setter,这意味着它们都被初始化为响应性的。因为真实的业务场景更复杂,当我们真的要用一个config来描述整个表单的时候,config的规模远远不止以上,整个配置对象的层次可能更深,也可能会造成一系列的性能问题。熟悉Vue2的同学都知道,在初始化的时候,get和set成为响应的时候会做一个数据的深度遍历来添加数据,组件执行render函数的时候会访问这些对象的属性。一旦被访问,就会触发数据属性的依赖收集动作。如果无脑属性太多,这个get方法就会无脑执行。这绝对不符合我们的初衷。怎么优化呢?这里用的是超大处理方式。深入阅读过Vue2源代码的人对属性__ob__并不陌生。上面的截图也有这个属性,但是你发现了吗?此ob属性没有相应的get或set。让我们打开源代码,看看尤达都做了些什么。首先,在响应处理之前,调用了一个def方法,这里的第四个参数没有传递:再看def的具体实现,其实就是在重新定义这个对象的属性。__ob__的可枚举此时为fals,因为不传输可枚举。这种治疗的作用是什么?总之,这个属性不能被遍历,在后续的响应式初始化中会被跳过。不清楚的伙伴可以看看作者写的一个demo加深理解:这里,同样的方法用于以非响应方式优化我们的配置。实际上,整个配置数据,我们只需要确保值是响应性的,许多其他描述性数据都是不必要的。接下来,优化其他领域。
//optimize function function optimize { return array . reduce = > { for){ if continue//非反应式优化所有非值属性object . define property } ACC . pushreturnacc },[])}
我们不具体展开介绍函数实现。此时,我们将再次打印config,看看它变成了什么:
以上,都体会到了!


