前端配置化真香~上班又多了60%的摸鱼时间

前端大学

共 9367字,需浏览 19分钟

 ·

2022-05-27 14:36

这篇文章是笔者曾经全盘负责了接近一年的广告投放系统沉淀下来的开发经验,大家各取所需,不喜勿喷~当然啦,有自己的见解、更好的建议的大牛朋友们,我们评论区见~hhh,大🔥多提点高见,让笔者继续学习继续进步!

本文从 场景介绍 、 设计&实现 、 性能优化 三个部分进行讲解。笔者当时的技术栈是 vue2 + element-ui,文章案例也是(其实大家不必纠结于技术栈,掌握设计的思路和理念,什么框架都是一样的)。主要能解决的问题就是 提高代码复用能力、提升开发效率,特别是需要开发多个大型表单系统的,配置化可以极大的提升效率,让你上班摸鱼不再是梦想!为了早点下班,我们接着往下看吧!

一、场景介绍

1. 业务场景

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

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

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

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

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

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

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

2. 配置化想法萌生

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

  • 高复用:如果说投放系统只需要接入一个渠道,那真的写 template 一把梭就完了。但事实上却不是这样的,当你接入了第一个 facebook ,你发现后面还有 tiktok 、 巨量引擎 、 广点通 等各种媒体渠道...
  • 可维护:并不是把东西做出来就完事了。首先,渠道方会有新配置功能推出,这个是不可控的。其次,系统开发时并不是全字段接入,而是先接入业务方所需要的核心配置,所以后期会有很多接入新的字段需求。

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

  1. 表单1。代码如下:
<el-form ref="form" :model="form"> 
  
  <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-input
  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
  
  <el-form-item label="活动2">
    <el-select 
      v-model="form.area" 
      :disable="form.name" 
      multiple 
    >
...el-input
  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-input
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: {
      multipletrue
    }
  }
]
复制代码

是不是有点内意思了?如果把 开发巨型表单系统 转换成 编写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 节点
)
复制代码

接着,我们直接开干: