Loading...
墨滴

老朽

2021/04/09  阅读:19  主题:绿意

写前端就是写表单?


写前端就是写表单?

首先,大家思考一下表单是什么?前端又是什么?


表单是什么?

小弟不才,曾有幸参与某中台的表单引擎的开发工作,一般开发前我们都是先用领域设计模型分析一波,时间的关系,先上图

用现在流行的八卦文翻译一下就是要先找出业务的聚合根,再分析它的属性,然后再总结它的生命周期

最后我们得出的结论是:

写前端等于写表单

本文终。

前端是什么?

那写前端到底是写什么? 本人才疏学浅 我认为人机需要交互的地方都属于前端 包括但不限于全息控制、人脑控制、声音控制、AR、VR 所以说要立志要做大前端的那位 你们以后的css样式可能还要加上声音/气味属性了

.flower:{
  color: yellow;
  sound: quiet;
  smell: sweet;
}

我设计的表单

但相信大家都同意的是,表单开发在日常的开发中,应该是最复杂,占用的时间应该是最多的 我先来随便说几点:

  • 动态表单生成
  • 多级联动
  • 动态校验
  • 定制业务组件
  • 嵌套子级表单
  • 数据反显
  • 编辑状态禁用
  • 等等

某知名博文《写前端就是写表单?》的博主曾经说过:“控制最小边界,组合,就是好架构” 所以我们先设计一个最小的表单

最小的表单

1.要有一个<form></form> 2.要有一个formData 3.要有一个控件配置表

配置表

建一个form-register.ts 的文件

ts, 是的我加了ts 就是将所有组件import进来 再export出去 规范点的话还能用require.context动态引入

import Text from './Text/index.vue'
//类型
export  formConfigs:formTypes= {
  text: Text
}

眼利的朋友应该发现有一个formTypes

//组件的class很强大,可以直接作为类型
export interface formTypes {
  Text?: Text
}

那还有一个最关键的别人调用的配置项是长怎样的呢,以下是我的参考,具体可以根据自己业务需要自行添加

export interface fieldType {
  fieldType: keyof formTypes
  key: string
  dict?: string
  type?: string
  name?: string
  label?: string
  placeholder?: string
  required?: boolean
  isShow?: Function //是否显示
  dateType?: 'date' | 'time' | 'year-month' | 'month-day' | 'datetime'
  readonly?: boolean
  rules?: rule[]
  isWrap?: boolean
  hiddenLabel? :boolean
  extraKey?: string | extraKeyType[]
  formDataFormatter?: Function //格式化函数
  formDataConvert?: Function // 转换成多个其他值
  propShowValue?:string //显示的字段
  handleChange?: Function
  [k: string]: any
}

大家发现会有一个[k: string]: any,需然牺牲了一些类型约束,但是换来的扩展自由也是很有价值的

组合

加入了一个动态组件渲染

<form>
  <template v-for="fieldItem in fieldList">
    <component
      :key="fieldItem.key"
      :is="formRegister[fieldItem.fieldType]"
      :fieldItem="fieldItem"
      v-bind="fieldItem"
      v-model="formData[fieldItem.key]"
    >
</component>
  </template>

</form>

这里用到v-bind把所有属性props进去,react的话可以自己解构...props 到这里如无意外的话,你的表单组件应该渲染出来了 但有人可能就会问,也不是杠什么的,你的fieldList是怎样来的

函数生成器

当初设计的时候第一直觉,就是props一个fieldList的数组进来 但后来遇到一些问题

  • 组件之间联动
  • 数据值强绑定
  • 表单项动态生成
  • 校验规则条件动态变化
  • 等 可能都要监听formData值的变化,再改变fieldList的值

后来,某天发呆的时候,想到《上帝掷骰子吗》一书中,对多宇宙的一个解说是,每个世界线的变动都会再产生出一个新宇宙

好家伙,那我们为什么不能写一个多宇宙的表单组件呢?每次formData值的变动就再产生出一个新的fieldList fieldListMaker诞生 我马上改成如下:

  get formDataString() {
    return JSON.stringify(this.formData)
  }
  @Watch('formDataString', { immediatetruedeepfalse })
  formDataChanged(val: any, oldVal: any) {
    if (val !== oldVal) {
      this.fieldList = this.fieldListMaker(val)
    }
  }

formData转为string监听,为了避免一些深层次的属性变化监听不到 传入的时候fieldListMaker长这样

fieldListMaker(formData: any): Array<fieldType> {
  // 你在这里可以做你想做的各种东西😊
  return [
    {
      fieldType'text',
      key'abc',
      label'def'
    }
  ]
}

总结

实际使用就是

import FormList from '@/components/form/index.vue'
<FormList
  ref="formRef"
  :fieldListMaker="fieldListMaker"
  :showBtn="false"
  @change="handleFormChange"
/>

大家可以根据各自喜好,在里面添加一些方法,如:

  1. 校验方法
  2. 设置属性值方法
  3. 清空校验方法
  4. 强制更新list方法
  5. 等 各种业务表单组件也可以自己添加,只要约定好一些基本方法,里面@Model('change')出来一个双向绑定就好了

真.完结,撒花

彩蛋

有兴趣的可以看源代码多宇宙表单[1]

参考资料

[1]

代码地址: https://github.com/hundren/multiverse-form

老朽

2021/04/09  阅读:19  主题:绿意

作者介绍

老朽