基于vue通用表单解决方案的思考与分析


Posted in Javascript onMarch 16, 2019

前言

“那要怎么改?”,“那得改到什么时候?”,“什么时候才能支持这些功能?”。

再一次听到了这样的话,我沉默了。到底要怎样改,这也是我所思考的,最近一直忙于其他,已经有一段时间没有处理 issue 了,趁着调休,我也要好好思考下。

半年前,接触了 el-form-renderer  ,瞬间感觉减轻了大部分表单编写的工作,一个简单的JSON配置,立刻展现出一个功能完好的表单页面。然而,随着使用的频率增加,却慢慢开始暴露各种不足,该组件的作者也不再单独对该组件进行维护了。由于项目表单场景的需要,我们 fork 了一个版本,但后来发现我们更想要的是一套属于我们自己的表单渲染规则,于是在我们的 Github 组织下建立了属于我们的代码仓库 @femessage/el-form-renderer  (以下都称为 el-form-renderer ),并开始了我们自己维护之路。

3W

el-form-renderer 是什么?为什么?怎么样?通过3W分析来看看我们做了什么。

WHAT?

基于 element-ui 封装的 表单渲染器 ,但不仅仅是 element-ui ,甚至不仅仅是表单。

基于vue通用表单解决方案的思考与分析

WHY?

这里的为什么有两层,第一层是为什么需要表单渲染器?第二次是什么是 el-form-renderer ?

借用《NoForm - 一个更好的表单解决方案》中的一句话:

做 B类业务 的同学应该深有感触,我们日常需要面对大量操作类或者表单类的场景,因此只要是能从这些重复的CRUD解放出来的方案,就是最好的方案

我们的目的也很简单,让天下没有难做的表单。

至于为什么是 el-form-renderer ,理由很简单,我们正在让没有难做的表单变成现实,至少在目前数十个项目中,解决了70%以上的表单方面的需求。

HOW?

如上文所说,如下图所见:

基于vue通用表单解决方案的思考与分析

一个好的表单需要解决的问题:

•表单的取值、赋值、校验

•表单联动

•自定义表单项

•表单项的事件、属性、slot

•对用户足够友好

所谓”下层基础决定上层建筑“,我们没有执着于从0开始,而是“让专业的人做专业的事”。基础组件, element-ui 足够专业,也正因为它的专业性,才让 el-form-renderer 最初的版本有了用武之地。然而目前远远不够的是,我们没有很好的处理表单联动和表单项的事件、slot的问题,这也正是我所要思考的地方。

基础用法

先来看看目前我们的实现(更多请参考 文档 )

<template>
 <el-form-renderer label-width="100px" :content="content" />
</template>
<script>
import UploadToAli from '@femessage/upload-to-ali'
export default {
 data () {
 return {
 content: [
 {
 $id: 'avatar',
 label: '头像',
 component: UploadToAli
 },
 {
 $id: 'username',
 label: '用户名',
 $type: 'input',
 $el: {
 placeholder: '请输入用户名'
 }
 }
 ]
 }
 }
}
</script>

效果如下:

基于vue通用表单解决方案的思考与分析

没有复杂的逻辑,只需进行简单配置 JSON 的方式就可实现常用表单功能

解决方案

因为不是从0开始,所以一开始作者的设计只服务于 element-ui 已有的组件。

之前的方案解决了什么

• 表单的取值 getValue ,赋值 updateValue

• 完整继承了 element 的 form 表单属性,包括校验

• 表单联动 $enableWhen

大部分简单的场景已经覆盖,但是局限于 element-ui 组件,没有处理好动态组件选项(如下拉选项)的问题,无法批量赋值,必须手动去空格...

现在的方案优化了什么

• 支持自定义组件,摆脱对 element-ui 的完全依赖(仍然依赖它的 el-form , el-form-item )

• 通过 setOptions 方法,动态的处理组件选项问题

• 添加批量更新数据方法 updateForm ,并添加 trim 来处理值

• 为了方便在其他组件中的集成和设置/获取值的需求,还添加了 inputFormat 、 outputFormat ( issue )

它本已完成了一个华丽的蜕变,甚至成为了我司组件的桥梁地位,然而,面对千奇百怪的需求,它确实还不够。

存在的问题

基于vue通用表单解决方案的思考与分析 

目前的7个 issue ,大致可以分为一下几类:

•表单联动

•自定义slot位置

•自定义事件

•其他优化

需求告诉我们,它还有很大的进步空间。

思考

回到开始的那些话,面对存在的问题,我们要怎么处理好它。

今天收到一个 PR ,来自公司的一位同事,处理的是 el-form-renderer 中 slot 位置的问题,默认只有 default ,显示在最后,有需求希望能显示在指定某一个表单项的前/后。该PR以表单项的 $id 为具名插槽,渲染该插槽内容到对应 $id 表单项的上方。

一个很好的思路,但是也让我思考了很多,或许它还是没有达到我的要求。

不妨再思考一个场景,表单的第一项和第三项渲染的上方需要渲染两个内容相同的 slot ,按上面的思路,我们应该写两个template,并分别定义他们要渲染到的位置的的 $id 。

上面的问题并不难解决,定义一个字符串匹配规则,或者在某一项的配置项中添加要渲染的slot,名字匹配则渲染,以达到复用的目的。

问题似乎是解决了, issue 可以关闭了,但是我们回过头来想想,我们为什么要自定义 slot 位置?

因为有issue,有用户有这个需求。

那他为什么会有这个需求?

我们不得而知,场景很多,但我们可以大胆的猜测,缺乏一个组件可以满足他的渲染需求,他需要 slot 来自定义展示内容。

所以,似乎我们需要的并不是 slot ,而是我们缺乏了那些组件,或者我们需要一个更通用的渲染方式来渲染我们的内容。很容易,我们想到了 render ,如果我们返回的是一个 render ,那似乎大部分 issue 可以关闭了。然而事实是,我们从开始就不希望出现 render ,因为它一点都不友好,甚至对部分人来说,它不简单,这不是我们的目的。 我们的目的是为了更好用,更好理解,就向我们的文档一样,它很简单,但很实用 。

许多PR,或者打算提PR的人忽略了一个问题,我们的组件没有支持事件,它很难实现?不,至少已经实现了绑定属性,绑定事件并不会多难,但是没有去支持它,因为我们 思考的是它的必要性 ,表单项是否真的需要绑定事件。

从一开始就说过,不只是 element-ui ,甚至不只是表单。我们的目的不纯粹,我们寻找的是通用的方案,如果支持了事件,表单项与业务代码的关联性绝对会更强,这不是我们希望看到的,至少在我们目前可以看得到的通过可视化的界面生成表单的前提下,我们不希望出现自定义事件的需求,它让我们通过可视化的界面生成表单变得不那么通用了。

那么,在目前的情况下,真的没办法解决这些问题了吗?答案是否定的。

已知我们可以通过自定义组件的方式拓展我们的表单项,那么我们也可以通过自定义组件解决我们遇到的 issue

import CustomComponent from './custom-component'

export default {
 data () {
 return {
 content: [
 {
 label: '用户名',
 component: CustomComponent,
 $id: 'username',
 $el: {
 placeholder: '请设置您的登陆用户名'
 }
 }
 ]
 }
 }
}

或者在一些更简单的场景

import UploadToAli from './UploadToAli'

export default {
 data () {
 return {
 content: [
 {
  $id: 'avatar',
  label: '图片',
  component: {
  data () {
  return {
  imgUrl: ''
  }
  },
  render: function (h) {
  return h(UploadToAli, {
  props: {
   value: this.imgUrl
  },
  on: {
   input: (val) => {
   this.imgUrl = val
   this.$emit('input', val)
   }
  }
  }, [
  h('p', {
   slot: 'spinner'
  }, '开始上传中...')
  ])
  }
  }
 }
 ]
 }
 }
}

如果从这个层面来说,我们早已经解决问题了,但是那些 issue 依然在那儿。还记得之前提过的对用户足够友好和 render 的事吗,目前来看,它能解决问题,但不是一个好的方案,解决问题并没有那么难,难的是解决了还能足够友好,足够简单,这也是我们一直在努力的方向。

结语

不知不觉写了好长...

通过配置的方式实现一个表单似乎是一个不错的思路,目前已经在公司中后台有过数十个页面的尝试。然而业务场景千变万化,我们没有办法解决100%的需求,但希望我们的方式为配置性表单能带来更多的思考。

抛砖引玉,最后贴一次仓库地址: https://github.com/FEMessage/el-form-renderer ,希望更多的方案和实现浮出水面,解放生产力。再次感谢 原作者

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JavaScript 封装Ajax传递的数据代码
Jun 05 Javascript
PPK 谈 JavaScript 的 this 关键字 [翻译]
Sep 29 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
Jan 08 Javascript
jquery实现带复选框的表格行选中删除时高亮显示
Aug 01 Javascript
js获取客户端外网ip的简单实例
Nov 21 Javascript
JQuery控制div外点击隐藏而div内点击不会隐藏的方法
Jan 13 Javascript
jquery模拟进度条实现方法
Aug 03 Javascript
基于touch.js手势库+zepto.js插件开发图片查看器(滑动、缩放、双击缩放)
Nov 17 Javascript
JS实现的随机排序功能算法示例
Jun 09 Javascript
vue 路由页面之间实现用手指进行滑动的方法
Feb 23 Javascript
vue使用vue-i18n实现国际化的实现代码
Apr 08 Javascript
jquery实现动态添加附件功能
Oct 23 jQuery
vue+php实现的微博留言功能示例
Mar 16 #Javascript
一些你可能不熟悉的JS知识点总结
Mar 15 #Javascript
使用element-ui table expand展开行实现手风琴效果
Mar 15 #Javascript
element-ui组件table实现自定义筛选功能的示例代码
Mar 15 #Javascript
vue过滤器用法实例分析
Mar 15 #Javascript
vue v-for循环重复数据无法添加问题解决方法【加track-by='索引'】
Mar 15 #Javascript
详解vue移动端项目代码拆分记录
Mar 15 #Javascript
You might like
php中文字母数字验证码实现代码
2008/04/25 PHP
ThinkPHP3.1新特性之字段合法性检测详解
2014/06/19 PHP
jQuery DIV弹出效果实现代码
2009/07/03 Javascript
Javascript面向对象之四 继承
2011/02/08 Javascript
JS控制网页动态生成任意行列数表格的方法
2015/03/09 Javascript
jquery实现点击展开列表同时隐藏其他列表
2015/08/10 Javascript
纯javascript实现分页(两种方法)
2015/08/26 Javascript
JS封装的自动创建表格的实现代码
2016/06/15 Javascript
json对象与数组以及转换成js对象的简单实现方法
2016/06/24 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
Vue2几种常见开局方式详解
2017/09/09 Javascript
angular4实现tab栏切换的方法示例
2017/10/21 Javascript
Bootstrap框架建立树形菜单(Tree)的实例代码
2017/10/30 Javascript
jQuery each和js forEach用法比较
2019/02/27 jQuery
JavaScript 处理树数据结构的方法示例
2019/06/16 Javascript
JavaScript如何使用插值实现图像渐变
2020/06/28 Javascript
用Python实现QQ游戏大家来找茬辅助工具
2014/09/14 Python
python递归删除指定目录及其所有内容的方法
2017/01/13 Python
python万年历实现代码 含运行结果
2017/05/20 Python
详解Python import方法引入模块的实例
2017/08/02 Python
python使用xpath中遇到:到底是什么?
2018/01/04 Python
Django 静态文件配置过程详解
2019/07/23 Python
Django Rest framework认证组件详细用法
2019/07/25 Python
python按行读取文件并找出其中指定字符串
2019/08/08 Python
python 实现人和电脑猜拳的示例代码
2020/03/02 Python
Europcar葡萄牙:葡萄牙汽车和货车租赁
2017/10/13 全球购物
意大利一家专营包包和配饰的网上商店:Borse Last Minute
2019/08/26 全球购物
JD Sports荷兰:英国领先的运动时尚零售商
2020/03/13 全球购物
请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
2015/07/16 面试题
农村婚礼主持词
2014/03/13 职场文书
旅游节目策划方案
2014/05/26 职场文书
同志主要表现材料
2014/08/21 职场文书
党的群众路线教育实践活动对照检查材料范文
2014/09/24 职场文书
销售员岗位职责范本
2015/04/11 职场文书
反邪教观后感
2015/06/11 职场文书
2016形势与政策学习心得体会
2016/01/12 职场文书