赞
踩
构造页面时需要给其他组件一个容器来包裹,先用 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 组件包含两部分: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 封装好,会使用到几乎所有的设置器。
由于时间关系,先只暴露 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 就会正常加载,项目也就不报错了。
这种方法有个缺点,在组件库封装过程中,其实是看不到效果的,因为渲染不出来。只有在具体使用组件库的时候,才会渲染出来,调试不方便。
总结
其实自定义封装组件,总结一下就三步:
在 src/components 文件夹下新建组件的文件夹,写逻辑代码,定义需要对外暴露的 props 。
在 根目录/index.tsx 中注册组件。不注册的话页面上看不到。
运行 npm run lowcode:dev 命令,会在 根目录/lowcode 目录下自动生成组件的描述文件 meta.ts,简单类型的 props 比如 string、bool 一般没啥问题,如果是复杂类型,比如复杂对象、数组,自动生成的描述可能不是我们想要的,这时需要手动改描述文件。
前两步我们都比较熟悉,重点主要在第三步改描述文件。在页面上对组件进行拖拽、配置时,支持的操作都是由描述文件定义的。描述文件的重点是设置器,一个属性支持怎样的交互,是可以输入文字,还是下拉框,还是可增删的数组,都是由设置器定义的。
设置器 Setter 的文档在 这里,里面包含了所有官方提供的 Setter。据平时的经验看,官方的设置器能满足 90% 的日常需求。当然还支持自定义 Setter,这部分我还没研究,可以查看官方文档。
官方的 demo 又更新了,新增了 antd 所有组件的支持,如果没有特殊需求,直接用官方提供的组件省时省力。
这个低代码引擎感觉还是在原型阶段,官方的文档、demo 会时不时更新,及时关注 crmeb 可能会有意外收获。
源码附件已经打包好上传到百度云了,大家自行下载即可~
链接: https://pan.baidu.com/s/14G-bpVthImHD4eosZUNSFA?pwd=yu27
提取码: yu27
百度云链接不稳定,随时可能会失效,大家抓紧保存哈。
如果百度云链接失效了的话,请留言告诉我,我看到后会及时更新~
码云地址:
http://github.crmeb.net/u/defu
Github 地址:
http://github.crmeb.net/u/defu
开源不易,Star 以表尊重,感兴趣的朋友欢迎 Star,提交 PR,一起维护开源项目,造福更多人!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。