165. 精读《数据搭建引擎 bi-designer API-组件》

bi-designer 是阿里数据中台团队自研的前端搭建引擎,基于它开发了阿里内部最大的数据分析平台,以及阿里云上的 QuickBI。

bi-designer 目前没有开源,因此文中使用的私有 npm 源 @alife/bi-designer 是无法在公网访问的。

本文介绍 bi-designer 组件的使用 API。

组件加载

组件实例定义在元信息 - element 中:

import { Interfaces } from "@alife/bi-designer";
const componentMeta: Interfaces.ComponentMeta = {
  element: () => <div />,
};

异步加载

使用 React.lazy 即可实现异步加载组件:

import { Interfaces } from "@alife/bi-designer";
const componentMeta: Interfaces.ComponentMeta = {
  // 懒加载
  element: React.lazy(async () => import("./real-component")),
};

懒加载的组件会自动完成加载,如需自定义加载 Loading 效果,可以阅读 组件异步、错误处理 文档。

组件异步、错误处理

  • 组件源码异步加载或者进行 Suspense 取数时,会调用 ComponentMeta.suspenseFallback 渲染。

  • 组件渲染出错时,会调用 ComponentMeta.errorFallback 渲染。

异步加载

import { Interfaces } from "@alife/bi-designer";
const SuspenseFallback: Interfaces.InnerComponentElement = ({
  componentInstance,
  componentmeta,
}) => {
  return <span>Loading</span>;
};
const componentMeta = {
  componentName: "suspense-custom-fallback",
  element: React.lazy(async () => {
    await sleep(2000);
    return Promise.resolve({ default: () => null });
  }),
  suspenseFallback,
};

上面例子中,对异步加载的组件定义了 suspenseFallback 来处理异步中的状态。

错误处理

import { Interfaces } from "@alife/bi-designer";
const errorFallback: Interfaces.ErrorFallbackElement = ({
  componentInstance,
  componentmeta,
  error,
}) => {
  return <span>错误:{error.toString()}</span>;
};
const componentMeta = {
  componentName: "error-custom-fallback",
  element: () => {
    throw new Error("error!");
  },
  errorFallback,
};

上面例子中, errorFallback 处理了组件抛出的任何错误。

  • error :当前组件报错信息。

容器组件

容器元素可以被拖入子元素,只要将 isContainer 设置为 true 即可:

export const yourComponentMeta: Interfaces.ComponentMeta = {
  componentName: "yourComponent",
  element: YourComponent,
  isContainer: true,
};

之后可以从 props.children 访问到子元素:

const YourComponent = ({ children }) => {
  return <div>{children}</div>;
};

多插槽容器组件

多插槽容器即一个容器内部有多个位置可响应拖拽。

实现多插槽容器组件注意两点即可:

  1. 这个大容器组件本身不为容器类型,因为我们要拖入到子元素,不需要拖入到它自己本身。

  2. 内部通过 ComponentLoader 添加容器类组件作为子元素。

比如我们要利用 Antd Card 实现一个多插槽容器,首先把 Card 申明为普通组件:

export const cardComponentMeta: Interfaces.ComponentMeta = {
  componentName: "card",
  element: CardComponent,
};

在实现 Card 功能时,我们在两处内部可拖拽区域调用 ComponentLoader 加载一个事先定义好的容器组件 div :

import { ComponentLoader, useDesigner } from '@alife/bi-designer'
const CardComponent: Interfaces.ComponentElement = () => {
  const { useKeepComponentLoaders } = useDesigner()

  useKeepComponentLoaders(['1'])

  return (
    <Card
      actions={[...]}
    >
      <ComponentLoader
        id="1"
        componentName="div"
        props={
   {style: { minHeight: 30 }}}
      />
    </Card>
  );
};

总结一下,我们可以利用 ComponentLoader 在组件内部加载任意组件,如果加载的是容器组件,就相当于增加了一块内部插槽。这种插槽可以插入理论上无数种容器组件,根据业务需求而定,比如上面这种最简单的 div 容器,可以是这么实现的:

const Div: Interfaces.ComponentElement = ({ children, style }) => {
  return (
    <div style={
   { width: "100%", height: "100%", ...style }}>{children}</div>
  );
};

Tabs 容器组件

Tabs 容器可以看作动态数量的多插槽容器:

import { ComponentLoader, useDesigner } from "@alife/bi-designer";
const TabsComponent: Interfaces.ComponentElement = ({ tabs }) => {
  const { useKeepComponentLoaders } = useDesigner();

  useKeepComponentLoaders(tabs?.map((each) => each.key));

  return (
    <div>
      <Tabs>
        {tabs?.map((each) => (
          <Tabs.TabPane tab={`Tab${each.title}`} key={each.key}>
            /* 举个例子,拿 div 这个组件作为 TabPane 的容器 */
            <ComponentLoader id={each.key} componentName="div" />
          </Tabs.TabPane>
        ))}
      </Tabs>
    </div>
  );
};

Tabs 根据配置动态渲染 TabPane ,为每个 TabPane 塞入一个容器即可。

注意, useKeepComponentLoaders 函数可以让数据变化后某个子 Tab 消失时,及时做画布脏数据清除。另外即便数据不是动态的,也要及时更新这个函数,比如某次更新, ComponentLoader id 为 3 的值从代码移除了,也要把 3 这个 id 从 useKeepComponentLoaders 中移除。

组件宽高

对于能自适应高度的组件,最佳方案是设置 100% 的宽高:

import { Interfaces } from "@alife/bi-designer";
const CustomComponent: Interfaces.ComponentElement = () => {
  return <div style={
   { width: "100%", height: "100%", minHeight: 50 }} />;
};

流式布局下 height: '100%' 高度会坍塌,因此可以设置个最小高度固定值兜底,或者通过 props 让用户配置。

如果组件不支持自适应宽高,比如渲染 canvas、svg 等图表时,需要自己监听宽高,或者利用 容器拓展组件 props 功能,在容器算好宽高具体值,再传入组件。

当然也可以直接设置一个默认高度,或者根据内容动态撑开组件,在流式布局、磁贴布局下可以自动撑开容器(磁贴布局编辑模式下拖拽的高度允许被运行时自动撑大),在自由布局下无法撑开,会出现内滚动条。

组件配置默认值

组件配置表单的默认值在 ComponentMeta.props 中定义:

import { Interfaces } from "@alife/bi-designer";
const componentMeta: Interfaces.ComponentMeta = {
  props: [
    {
      name: "title",
      defaultValue: "标题",
    },
  ],
};

Props 描述了组件入参信息,包括:

interface MetaProps {
  /**
   * 属性名
   */
  name: string;
  /**
   * 属性类型
   */
  type?: string;
  /**
   * 属性描述
   */
  description?: string;
  /**
   * 默认值
   */
  defaultValue?: any;
}

如果只设置默认值,只需要关心 name 和 defaultValue 。

组件配置表单

组件配置表单在 ComponentMeta.propsSchema 中定义:

import { Interfaces } from '@alife/bi-designer'
const componentMeta: Interfaces.ComponentMeta = {
  platform: 'fbi', // 平台名称
  propsSchema: {
    style: {
      color: {
        title: 'Color',
        type: 'color',
        redirect: 'color',
      },
    },
  },
}
  • platform :项目类型。不同项目类型的 propsSchema 结构可能不同,其他取数逻辑可能也不同。

  • propsSchema :表单配置结构,符合 UISchema 规范。对于特殊表单可能使用自己的规范。

组件配置修改回调

组件配置修改回调在每次组件实例信息被修改时触发,在 ComponentMeta.onPropsChange 中定义:

import { Interfaces } from "@alife/bi-designer";
const componentMeta: Interfaces.ComponentMeta = {
  onPropsChange: ({ prevProps, currentProps, componentMeta
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值