基于react的表单生成器FormRender

本文是对阿里FormRender的分析。

FormRender 1.0 是下一代的 React.js 表单解决方案。项目从内核级别进行了重写,为了能切实承接日益复杂的表单场景需求。我们的目标是以强大的扩展能力对表单场景 100%的覆盖支持,同时保持开发者能快速上手,并以表单编辑器、插件、自定义组件等一系列周边产品带来极致的开发体验。

FormRender配套有在线拖拉拽的工具,见fr-generator。

FormRender和fr-generator两套工具配合起来,基本满足了项目的初始需求,因需要扩展,或许还会对源码进行改造,在此先深入研究了下[email protected]的源码,后面再研究fr-generator。

1 使用举例

1.1 schema

{
  "type": "object",
  "properties": {
    "input_xnNHW9": {
      "title": "输入框",
      "type": "string",
      "props": {}
    },
    "textarea_Ski8kS": {
      "title": "编辑框",
      "type": "string",
      "format": "textarea",
      "props": {}
    }
  },
 "column": 1, // 整体控制一行的列数,默认为1
  "labelWidth": 120, // 整体控制标签宽度
  "displayType": "row" // 整体控制表单元素与 label 同行 or 分两行展示, inline 则整个展示自然顺排,有'column'、'row'和'inline'三个选项
}

1.2 渲染

效果图

2 整体架构

整体架构图

3 核心

Core

3.1 Core

组件经过简单预处理后将schema和表单布局等信息传给,而MCore = React.memo(CoreRender, areEqual),会根据表单值、错误提示和schema等信息是否有变化来决定是否重新渲染(在[email protected]中去掉了MCore这一层):

const areEqual = (prev, current) => {
  if (prev.allTouched !== current.allTouched) {
    return false;
  }
  if (prev.displayType !== current.displayType) {
    return false;
  }
  if (prev.column !== current.column) {
    return false;
  }
  if (prev.labelWidth !== current.labelWidth) {
    return false;
  }
  if (
    JSON.stringify(prev._value) === JSON.stringify(current._value) &&
    JSON.stringify(prev.schema) === JSON.stringify(current.schema) &&
    JSON.stringify(prev.errorFields) === JSON.stringify(current.errorFields)
  ) {
    return true;
  }
  return false;
};
const MCore = React.memo(CoreRender, areEqual);

3.2 CoreRender

{_children}

3.2.1 _children:根据子元素的类型是object、数组或checkbox来决定调用的组件

// 计算 children
  let _children = null;
  if (hasChildren) {
    if (isObj) {
      _children = objChildren; // 即RenderObject组件
    } else if (isList) {
      _children = listChildren; // 即RenderList组件
    }
  } else if (isCheckBox) {
    _children = schema.title;
  }

3.2.2 CoreRender会调用RenderField组件渲染_children,RenderField的核心逻辑如下图:

RenderField

ExtendedWidget中有一个逻辑是通过Suspense包裹了组件,这大概是因为有一部分组件通过React.lazy做了处理,需要配套使用Suspense。
3.2.3 RenderList
当组件类型为list时,会根据具体情况分别渲染成不同组件:

switch (renderWidget) {
    case 'list0':
    case 'cardList':
      return ;
    case 'list1':
    case 'simpleList':
      return ;
    case 'list2':
    case 'tableList':
      return ;
    case 'list3':
    case 'drawerList':
      return ;
    case 'list4':
    case 'virtualList':
      return ;
    case 'tabList':
      return ;
    default:
      return ;
  }

3.2.4 RenderObject

const RenderObject = ({
  children = [],
  dataIndex = [],
  displayType,
  hideTitle,
}) => {
  return (
    <>
      {children.map((child, i) => {
        const FRProps = {
          displayType,
          id: child,
          dataIndex,
          hideTitle,
        };
        return ;
      })}
    
  );
};

RenderObject内部会递归调用Core组件,一层层找到子组件配置进行渲染。
schema有个配置类型是objecttypeobjecttitle为空,会被渲染成div,例如根元素;如果typeobject,但title不为空会被渲染成Collpase组件。
object类型往往配置有properties,用来定义子组件们。

你可能感兴趣的:(基于react的表单生成器FormRender)