天天看點

低代碼引擎實戰-從零封裝低代碼元件

上一篇文章講了如何開始使用阿裡低代碼引擎 low-engine,以及如何在引擎 demo 中引用自定義元件,本篇将基于 vant 和 antd 封裝一些低代碼元件,帶領大家熟悉自定義元件的封裝和注意事項。

建立低代碼的元件庫初始化項目參考文檔:lowcode-engine.cn/docV2/funcv…

上篇文章位址:

阿裡低代碼引擎 lowcode-engine 使用詳解 - 開發自定義元件并內建

一、 Container

構造頁面時需要給其他元件一個容器來包裹,先用 vant 的 Card 元件來封裝我們的容器元件 Container。

src/components

目錄下建立

Container

檔案夾,再建立

Container.tsx

index.tsx

檔案

Container.tsx

import * as React from 'react';
import {createElement} from 'react';
import {Card} from 'react-vant';
import './index.scss'

export interface ContainerProps {
  title?: string;
  style?: 'object'
  direction?: 'row' | 'column'
}

/**
 * 由 Card 組成的 container 容器
 * @param title
 * @param children
 * @param otherProps
 * @constructor
 */
const JContainer: React.FC<ContainerProps> = ({title, children, direction = 'column', style = {}, ...otherProps}) => {
  const _style = style || {} as any;
  _style.flexDirection = direction;
  const _otherProps = otherProps || {} as any;
  _otherProps.style = _style;
  
  return (
    <Card>
      {title && <Card.Header>{title}</Card.Header>}
      <Card.Body>
        <div className={'container-wrapper'} {..._otherProps}>
          {children}
        </div>
      </Card.Body>
    </Card>
  )
}

export default JContainer
複制代碼           

複制

direction

屬性是控制 Container 裡面元素的排列方式,對應 flex 布局的

flex-direction

屬性。

index.tsx

import Container from './Container'

export type {ContainerProps} from './Container'
export default Container;
複制代碼           

複制

然後在

src/index.tsx

導出

export type {ContainerProps} from './components/container'
export {default as Container} from './components/container'
複制代碼           

複制

運作指令

npm run lowcode:dev

會看到跟

src

同級的目錄

lowcode

目錄下多了個

container

檔案夾,裡面有個

meta.ts

檔案,這是根據代碼生成的元件描述檔案,在拖拽使用這個元件時,低代碼引擎根據這個描述檔案來解析元件。

import { ComponentMetadata, Snippet } from '@alilc/lowcode-types';

const ContainerMeta: ComponentMetadata = {
  "componentName": "Container",
  "title": "Container",
  "docUrl": "",
  "screenshot": "",
  "devMode": "proCode",
  "npm": {
    "package": "mini-elements",
    "version": "0.1.1",
    "exportName": "Container",
    "main": "src/index.tsx",
    "destructuring": true,
    "subName": ""
  },
  "configure": {
    "props": [
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "title",
            "zh-CN": "title"
          }
        },
        "name": "title",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      }
    ],
    "supports": {
      "style": true
    },
    "component": {}
  }
};
const snippets: Snippet[] = [
  {
    "title": "Container",
    "screenshot": "",
    "schema": {
      "componentName": "Container",
      "props": {}
    }
  }
];

export default {
  ...ContainerMeta,
  snippets
};
複制代碼           

複制

預設生成的描述檔案,可能不能滿足需求,需要拓展。

想要更多自定義配置,有兩種方式:

  • 在代碼中寫

    propTypes

    自動生成
  • 手動配置

定義好元件的 Props 之後,運作

npm run lowcode:dev

指令會根據目前定義的 props 自動生成描述檔案,基本類型自動生成的描述一般沒啥問題,但如果是複雜對象可能會描述不太準确。

注意這裡有個坑,隻有第一次運作以上指令才會自動生成描述檔案,如果這個元件的描述檔案已經存在,我們又修改了元件,再次運作指令則不會将新增的屬性追加進描述檔案中,換句話說以後都需要手動配置了。

有個小技巧可以減輕工作量,如果你沒有手動改過配置檔案,那修改元件源碼後,每次運作前把描述檔案删掉,就可以按照最新的 Props 自動生成新的描述檔案了。

但是如果按下面的方式手動配置過描述檔案,不建議删掉重新生成,之前手動配置的都會丢失。

更改

lowcode/contianer/meta.ts

,想要它成為一個容器,在

component

對象下設定 isContainer 即可。

如果想添加新的屬性,或者代碼中元件的 props 中定義的屬性沒有顯示出來,則需要手動新增 props。

direction

屬性想要枚舉值,隻有

row

column

兩個屬性值。查詢支援的設定器,發現

RadioGroupSetter

可以滿足需求,按照定義寫我們自己的屬性和設定器

{
  name: 'direction',
  description: '内容的排列方向',
  setter: {
    componentName: 'RadioGroupSetter',
    initialValue: 'column',
    props: {
      options: [
        'column',
        'row'
      ],
    }
  }
}
複制代碼           

複制

完整的定義如下:

import {ComponentMetadata, Snippet} from '@alilc/lowcode-types';

const ContainerMeta: ComponentMetadata = {
  "componentName": "Container",
  "title": "Container",
  "docUrl": "",
  "screenshot": "",
  "devMode": "procode",
  "npm": {
    "package": "mini-elements",
    "version": "0.1.1",
    "exportName": "Container",
    "main": "src/index.tsx",
    "destructuring": true,
    "subName": ""
  },
  "configure": {
    "props": [
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "title",
            "zh-CN": "title"
          }
        },
        "name": "title",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": false,
          "initialValue": ""
        }
      },
      {
        name: 'direction',
        description: '内容的排列方向',
        setter: {
          componentName: 'RadioGroupSetter',
          initialValue: 'column',
          props: {
            options: [
              'column',
              'row'
            ],
          }
        }
      }
    ],
    "supports": {
      "style": true
    },
    "component": {
      isContainer: true,
      nestingRule: {
        // 允許拖入的元件白名單
        // childWhitelist: ['ColorfulButton', 'Button'],
        // 同理也可以設定該元件允許被拖入哪些父元件裡
        // parentWhitelist: ['Tab'],
      },
    }
  }
};
const snippets: Snippet[] = [
  {
    "title": "Container",
    "screenshot": "",
    "schema": {
      "componentName": "Container",
      "props": {}
    }
  }
];

export default {
  ...ContainerMeta,
  snippets
};
複制代碼           

複制

效果如圖,可配置一個 title 屬性,如果有值則渲染 Header,沒有就不渲染。

還可選擇

direction

的值,預設 column。

裡面可以拖入其他元件,但僅限白名單裡的元件。

低代碼引擎實戰-從零封裝低代碼元件

二、Panel 元件

先看下效果圖,Panel 元件包含兩部分:Title 和 Content,重點突出 content 的内容。

右邊可配置的屬性為:

  • title: 标題
  • content:内容,一般為數字
  • flex: flex 布局下所占的份數,同 css 的 flex 屬性,預設 1
低代碼引擎實戰-從零封裝低代碼元件

src/components

下建立

panel

目錄,并建立

Panel.tsx

index.tsx

index.scss

三個檔案

// Panel.tsx
import React, { createElement } from 'react'
import './index.scss'

export interface PanelProps {
  title: string;
  content: string;
  flex: number;
}

const Panel: React.FC<PanelProps> = ({title, content, flex = 1, children, ...otherProps}) => {
  
  const _otherProps = otherProps || {} as any;
  // @ts-ignore
  _otherProps.style = otherProps.style || {} as any
  _otherProps.style.flex = flex;
  
  return (
    <div className={'panel'} {...otherProps}>
      <div className={'title'}>{title || 'Panel标題'}</div>
      <div className={'content'}>{content || 'Panel内容'}</div>
    </div>
  )
}

export default Panel


// index.tsx
import Panel from './Panel'
export type {PanelProps} from './Panel'
export default Panel;


// index.scss
@import "./src/variables";

.panel {
  display: flex;
  flex-direction: column;

  .title {
    font-size: 12px;
    color: $text-minor;
  }
  .content {
    font-size: 28px;
    font-weight: 500;
    color: $text-main;
  }
}
複制代碼           

複制

同樣需要修改生成的

lowcode/panel/meta.ts

檔案,一般來說如果隻是修改可配置的屬性,隻需改

configure.props

屬性即可。

import { ComponentMetadata, Snippet } from '@alilc/lowcode-types';

const PanelMeta: ComponentMetadata = {
  "componentName": "Panel",
  "title": "Panel",
  "docUrl": "",
  "screenshot": "",
  "devMode": "procode",
  "npm": {
    "package": "mini-elements",
    "version": "0.1.1",
    "exportName": "Panel",
    "main": "src/index.tsx",
    "destructuring": true,
    "subName": ""
  },
  "configure": {
    "props": [
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "title",
            "zh-CN": "title"
          }
        },
        "name": "title",
        "setter": {
          "componentName": "StringSetter",
          "isRequired": true,
          "initialValue": ""
        }
      },
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "content",
            "zh-CN": "content"
          }
        },
        "name": "content",
        setter: {
          componentName: 'MixedSetter',
          props: {
            setters: [
              'StringSetter',
              'VariableSetter',
            ],
          },
        }
      },
      {
        name: 'flex',
        setter: {
          componentName: 'NumberSetter',
          initialValue: 1
        }
      }
    ],
    "supports": {
      "style": true
    },
    "component": {}
  }
};
const snippets: Snippet[] = [
  {
    "title": "Panel",
    "screenshot": "",
    "schema": {
      "componentName": "Panel",
      "props": {}
    }
  }
];

export default {
  ...PanelMeta,
  snippets
};
複制代碼           

複制

三、Table 元件

在各種元件中,Table 元件是最複雜的了。要把 Table 封裝好,會使用到幾乎所有的設定器。

由于時間關系,先隻暴露

dataSource

columns

屬性,通過

columns

屬性,我們将學會如何使用

ArraySetter

動态設定數組。通過

dataSource

屬性,我們将學會使用

MixedSetter

使屬性支援多種設定方式。

本元件基于 antd 的 Table 擴充。

src/components

目錄下建立

Table

檔案夾,然後建立

Table.tsx

index.ts

檔案

import React, {createElement} from 'react'
import Table, {ColumnsType} from "antd/lib/table";

export interface JTableProps {
  columns: ColumnsType;
  dataSource: any[];
}

const JTable: React.FC<JTableProps> = ({columns, dataSource}) => {

  // 資料處理,防止字段為空
  const _columns = columns?.map((col, index) => {
    if (!col) {
      return {
        dataIndex: `${index}`,
        title: '列名'
      }
    }
    const {dataIndex, title} = col as any;
    return {
      dataIndex: dataIndex || `${index}`,
      title: title || '列名'
    }
  })

  return (
    <Table
      dataSource={dataSource}
      columns={_columns}
    />
  );
}


export default JTable
複制代碼           

複制

import Table, {JTableProps} from './Table'

export type {JTableProps}
export default Table;
複制代碼           

複制

别忘了在

src/index.tsx

上注冊元件,否則看不到效果。

export type {JTableProps} from './components/Table';
export {default as Table} from './components/Table'
複制代碼           

複制

運作

npm run lowcode:dev

,會在

根目錄/lowcode

下生成

table

檔案夾,裡面的

meta.ts

就是元件的描述檔案。

由于我們暴露出的屬性

dataSource

columns

是複雜結構,自動生成的描述不能滿足需求,是以手動更改描述檔案:

import { ComponentMetadata, Snippet } from '@alilc/lowcode-types';

const TableMeta: ComponentMetadata = {
  "componentName": "Table",
  "title": "Table",
  "docUrl": "",
  "screenshot": "",
  "devMode": "procode",
  "npm": {
    "package": "mini-elements",
    "version": "0.1.6",
    "exportName": "Table",
    "main": "src/index.tsx",
    "destructuring": true,
    "subName": ""
  },
  "configure": {
    "props": [
      {
        "title": {
          "label": {
            "type": "i18n",
            "en-US": "資料列",
            "zh-CN": "資料列"
          }
        },
        "name": "columns",
        "setter": {
          "componentName": "ArraySetter",
          "props": {
            "itemSetter": {
              "componentName": "ObjectSetter",
              "isRequired": false,
              "props": {
                config: {
                  items: [
                    {
                      "name": "dataIndex",
                      "setter": {
                        "componentName": "StringSetter",
                        "isRequired": true,
                        "initialValue": "id"
                      }
                    },
                    {
                      "name": "title",
                      "setter": {
                        "componentName": "StringSetter",
                        "isRequired": true,
                        "initialValue": "列名"
                      }
                    },
                  ]
                }
              },
            }
          },
          "isRequired": true,
          initialValue: [
            {
              dataIndex: 'id',
              title: 'ID'
            },
            {
              dataIndex: 'name',
              title: '姓名'
            },
            {
              dataIndex: 'age',
              title: '年齡'
            },
          ]
        },
      },
      {
        "name": "dataSource",
        setter: {
          componentName: 'MixedSetter',
          props: {
            setters: [
              'JsonSetter',
              'VariableSetter',
            ],
          },
        }
      }
    ],
    "supports": {
      "style": true
    },
    "component": {}
  }
};
const snippets: Snippet[] = [
  {
    "title": "Table",
    "screenshot": "",
    "schema": {
      "componentName": "Table",
      "props": {}
    }
  }
];

export default {
  ...TableMeta,
  snippets
};
複制代碼           

複制

效果如圖:

低代碼引擎實戰-從零封裝低代碼元件

columns

是一個數組,我們可以自由的加減列,是以需要用官方提供的

ArraySetter

,使用文檔 點這裡。每一個 item 都是一個

ObjectSetter

,說實話結構還挺複雜的。

dataSource

支援綁定資料源和直接寫 json,是以使用

MixedSetter

四、坑點

如果你用的是 antd 元件庫,那麼會遇到個大坑。

項目中用到了

@ant-design/icons

時,比如在一個元件中引用了某個 icon,會導緻元件渲染報錯

低代碼引擎實戰-從零封裝低代碼元件

原因是找不到這個圖示元件,查一下加載的 js 資源,發現并沒有加載

ant-design/icons

低代碼引擎實戰-從零封裝低代碼元件

沒想到自家的元件庫竟然不完全支援!測試發現其他的元件庫,像 vant、tea 等都沒有這個問題。

暫時還沒想到在元件庫層面的解決辦法,還沒找到手動注入

ant-design/icons

的入口。

但是在 demo 中用元件庫的時候,找到了解決方案。官方 demo 有個

assets.json

,這裡定義了引用的資源,我們可以手動把 icon 添加進去,這樣在項目運作時,

ant-design/icons

就會正常加載,項目也就不報錯了。

低代碼引擎實戰-從零封裝低代碼元件

這種方法有個缺點,在元件庫封裝過程中,其實是看不到效果的,因為渲染不出來。隻有在具體使用元件庫的時候,才會渲染出來,調試不友善。

總結

其實自定義封裝元件,總結一下就三步:

  1. src/components

    檔案夾下建立元件的檔案夾,寫邏輯代碼,定義需要對外暴露的 props 。
  2. 根目錄/index.tsx

    中注冊元件。不注冊的話頁面上看不到。
  3. 運作

    npm run lowcode:dev

    指令,會在

    根目錄/lowcode

    目錄下自動生成元件的描述檔案

    meta.ts

    ,簡單類型的 props 比如 string、bool 一般沒啥問題,如果是複雜類型,比如複雜對象、數組,自動生成的描述可能不是我們想要的,這時需要手動改描述檔案。

前兩步我們都比較熟悉,重點主要在第三步改描述檔案。在頁面上對元件進行拖拽、配置時,支援的操作都是由描述檔案定義的。描述檔案的重點是設定器,一個屬性支援怎樣的互動,是可以輸入文字,還是下拉框,還是可增删的數組,都是由設定器定義的。

設定器

Setter

的文檔在 這裡,裡面包含了所有官方提供的

Setter

。據平時的經驗看,官方的設定器能滿足 90% 的日常需求。當然還支援自定義 Setter,這部分我還沒研究,可以檢視官方文檔。

官方的 demo 又更新了,新增了 antd 所有元件的支援,如果沒有特殊需求,直接用官方提供的元件省時省力。

這個低代碼引擎感覺還是在原型階段,官方的文檔、demo 會時不時更新,及時關注可能會有意外收獲。