基于react的表單生成器FormRender

本文是對(duì)阿里FormRender的分析。

FormRender 1.0 是下一代的 React.js 表單解決方案。項(xiàng)目從內(nèi)核級(jí)別進(jìn)行了重寫,為了能切實(shí)承接日益復(fù)雜的表單場(chǎng)景需求。我們的目標(biāo)是以強(qiáng)大的擴(kuò)展能力對(duì)表單場(chǎng)景 100%的覆蓋支持,同時(shí)保持開發(fā)者能快速上手,并以表單編輯器、插件、自定義組件等一系列周邊產(chǎn)品帶來(lái)極致的開發(fā)體驗(yàn)。

FormRender配套有在線拖拉拽的工具,見fr-generator

FormRender和fr-generator兩套工具配合起來(lái),基本滿足了項(xiàng)目的初始需求,因需要擴(kuò)展,或許還會(huì)對(duì)源碼進(jìn)行改造,在此先深入研究了下FormRender@1.5.8的源碼,后面再研究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, // 整體控制一行的列數(shù),默認(rèn)為1
  "labelWidth": 120, // 整體控制標(biāo)簽寬度
  "displayType": "row" // 整體控制表單元素與 label 同行 or 分兩行展示, inline 則整個(gè)展示自然順排,有'column'、'row'和'inline'三個(gè)選項(xiàng)
}

1.2 渲染

效果圖

2 整體架構(gòu)

整體架構(gòu)圖

3 核心

Core

3.1 Core

<Core />組件經(jīng)過(guò)簡(jiǎn)單預(yù)處理后將schema和表單布局等信息傳給<MCore/>,而MCore = React.memo(CoreRender, areEqual),會(huì)根據(jù)表單值、錯(cuò)誤提示和schema等信息是否有變化來(lái)決定是否重新渲染<CoreRender />(在form-render@1.8.5中去掉了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

<div
      style={columnStyle}
      className={`${containerClass} ${debugCss ? 'debug' : ''}`}
    >
      <RenderField {...fieldProps}>{_children}</RenderField>
</div>

3.2.1 _children:根據(jù)子元素的類型是object、數(shù)組或checkbox來(lái)決定調(diào)用的組件

// 計(jì)算 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會(huì)調(diào)用RenderField組件渲染_children,RenderField的核心邏輯如下圖:

RenderField

ExtendedWidget中有一個(gè)邏輯是通過(guò)Suspense包裹了組件,這大概是因?yàn)橛幸徊糠纸M件通過(guò)React.lazy做了處理,需要配套使用Suspense。
3.2.3 RenderList
當(dāng)組件類型為list時(shí),會(huì)根據(jù)具體情況分別渲染成不同組件:

switch (renderWidget) {
    case 'list0':
    case 'cardList':
      return <CardList {...displayProps} />;
    case 'list1':
    case 'simpleList':
      return <SimpleList {...displayProps} />;
    case 'list2':
    case 'tableList':
      return <TableList {...displayProps} />;
    case 'list3':
    case 'drawerList':
      return <DrawerList {...displayProps} />;
    case 'list4':
    case 'virtualList':
      return <VirtualList {...displayProps} />;
    case 'tabList':
      return <TabList {...displayProps} />;
    default:
      return <CardList {...displayProps} />;
  }

3.2.4 RenderObject

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

RenderObject內(nèi)部會(huì)遞歸調(diào)用Core組件,一層層找到子組件配置進(jìn)行渲染。
schema有個(gè)配置類型是object,typeobjecttitle為空,會(huì)被渲染成div,例如根元素;如果typeobject,但title不為空會(huì)被渲染成Collpase組件。
object類型往往配置有properties,用來(lái)定義子組件們。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容