FormRender 1.0 升级到 2.0 经验总结

41,185 阅读4分钟

如果你是1.0的老用户,担心升级后踩坑,那么这篇文章可以帮你快速过渡升级中可能遇到的差异问题。

本文来自飞猪前端同学在进行FormRender2.0升级过程中,基于官方的 2.0升级方案 之上,做的一些差异化总结。

一些关键字处理上的差异

1. description 差异

2.0 的 description 使用了 tooltip 来替代,原有的 description 直接显示在了 label 后,不再用 icon 收拢。1.0 的 description 是在label后新增了一个 icon,鼠标 hover 到 icon 的时候通过 tooltip 组件展示了文案。

2. required 差异

2.0 会优先读取rules内部的 required,如果 rules 内部已有 required,则不必在 properties 层强制写上 required,同时 2.0 中 properties 层的 required 校验有其内部自己的处理逻辑,所以如果 rules 里有 required 了,就不要重复写了。 1.0 与 2.0 相反。1.0 中 properties 的 required 会覆盖 rules 层的 required 。

3. labelWidth 差异

2.0 labelWidth 支持 0 的写法,如果为 0 时 表单 label 宽度默认为 0,文字超出仍会溢出。1.0 不支持 0 的写法,>= 0 是支持的,文字超出不会溢出。

一些执行逻辑处理上的差异

1. schema结构中最外层非关键字的字段处理 差异。

2.0 会将非关键字的 key 输出到dom 上,如果关键字是驼峰的,会引起 react dom render 的报错。最好取消驼峰的写法。1.0的 schema 最顶层的非关键字是不处理的,

2. 组件销毁时,formData 数据处理的 差异。

2.0里,比如 cardList 类型的组件,在 hidden 或者销毁时,formData 也会同步销毁 cardList 内部的数据。在 1.0 中并不会销毁数据,只是在 submit 时不会将 hidden 的数据吐出,1.0 中需要通过 hooks 监听 手动销毁数据。

3. CardList 组件 min max 与 rules 内部的 min max 差异。

2.0 中 cardList 内部自带的 min max 校验 会与 rules 的校验同步进行,校验不通过时会导致出现两条 error 信息,1.0 中是优先处理内部自带的min max校验,若不通过则不会再执行 rules 校验。

4. properties 中 type 类型为 any 时的执行 差异。

2.0 的 type 为 any 类型时不再进行 FR 的内部的 required(必填) 校验。 1.0 时,会进行 required(必填)校验。如果在 2.0 要进行 required 校验,可以写在 rules 里。同时,2.0 的 type 优先级高于 rules 中的 type。

一些css或dom结构上的差异

1.0 的表单项dom结构如下

/* 一条表单项 */
/* schema 的 className 会在这一层,以xxx为例 */
<div class="fr-field w-100 flex xxx">
    /* labelWidth的实现方式是 width: 100px */
    <div class="fr-label fr-label-row fr-label-align-right flex-shrink-0 fr-label-row" style="width: 100px">
        表单组件label
    </div>
    <div class="fr-content fr-content-row flex-grow-1 relative" datapath="">
        /* 表单组件容器 */ 
        <div class="fr-item-wrapper">
            /* 表单组件内容 比如 */
            <Input />
        </div>
        /* 表单项底部间隙,默认展示该dom,出现错误校验信息时去除 */
        <div class="field-block"></div>
        /* 错误校验信息,默认无此dom,出现错误校验信息时才插入 */
        <div class="error-message">
            error校验信息
        </div>
    </div>
</div>

2.0 的表单项dom结构如下

/* 一条表单项 */
<div class="ant-col ant-col-24" style="padding-left: 8px; padding-right: 8px;">
    /* schema 的 className 会在这一层,以xxx为例 */
    <div class="ant-form-item fr-field xxx">
        <div class="ant-row ant-form-item-row">
            /* labelWidth的实现方式是 flex: 0 0 100px */
            <div class="ant-col ant-form-item-label ant-form-item-label-wrap" style="flex: 0 0 200px;">
                <label for="datapath" class="ant-form-item-required" title="表单组件label">
                    表单组件label
                </label>
            </div>
            /* 表单组件容器 */ 
            <div class="ant-col ant-form-item-control" style="flex: 1 1 auto;">
                <div class="ant-form-item-control-input">
                    <div class="ant-form-item-control-input-content">
                        /* 表单组件内容 比如 */
                        <Input />
                    </div>
                </div>
                /* 错误校验信息,默认无此dom,出现错误校验信息时才插入 */
                <div style="display: flex; flex-wrap: nowrap;">
                    <div id="datapath_help" class="ant-form-item-explain ant-form-item-explain-connected" role="alert">
                        <div class="ant-form-item-explain-error" style="">
                            error校验信息
                        </div>
                    </div>
                    <div style="width: 0px; height: 24px;"></div>
                </div>
                /* 错误校验信息,默认无此dom,出现错误校验信息时才插入 end ====> */
            </div>
            /* 表单组件容器 end ===> */
        </div>
    </div>
</div>
  1. 1.0 的自定义表单的组件容器dom层默认是没有宽度的,2.0 默认宽度为100%了
  2. 1.0 label 宽度为 width: 100px,2.0 则为 flex: 0 0 100px

其它

  1. 自定义组件内部不要再通过 addons.formData 获取整个 data 数据,通过 getValues() 获取。
  2. 2.0 中如果子组件为 FR ,子组件的 FR 须加上 component="div" 以避免 react render 报错。
  3. 2.0 不再优先使用 onValuesChange,使用 watch 替代。1.0 中支持 onValuesChange。

2.0 的一些好用的新玩法

2.0 自定义组件为子表单时提供了校验的钩子。

1.0 需要手动实现父子表单的校验触发,2.0 只需要绑定钩子即可:

useImperativeHandle(addons?.fieldRef, () => ({
    validator: async () => childForm.validateFields()
  }))

schema 全字段支持了表达式

相当于一个 rules 完成了通过表达式来执行指定的校验规则,校验规则可以是正则、内置函数、也可以是函数名!函数名指向methods。

{ 
    certNo: {
      title: "{{ formData.location == 'F' ?  '商业登记证号' : '统一社会信用代码' }}",
      type: "string",
      widget: 'CompanyNo',
      required: true,
      dependencies: ['location', 'merchantName', 'certTypeCode'],
      className: 'normal',
      rules:[
        {
          required: true
        },
        {
          pattern: "{{ formData.location == 'F' ? '^[a-zA-Z0-9-]+$' : '[^\u4e00-\u9fa5]+$' }}",
          message: "{{ formData.location == 'F' ? '商业登记证号只能有英文' : '统一社会信用代码不能有中文' }}"
        },
        {
          validator: (_, value, { form }) => {
            if (!value || form.getValueByPath('CompanyNo2') === value) {
              return true;
            }
            return false;
          }, 
          message: '你输入的工商信息与登记证号不匹配' 
        },
        {
          validateTrigger: 'onSubmit',
          validator: "validFn",
        }
      ]
    }
}

总结

2.0不仅解决了change量大时的性能卡顿的问题,其它的惊喜还是比较多的,比如支持了全量字段的表达式、rules玩法更惊艳,支持子表单钩子等。新的能力出来,1.0里需要手动实现的执行规则都可以汰换到schema中了,能省下不少的代码量。