bi-designer 是阿里数据中台团队自研的前端搭建引擎,基于它开发了阿里内部最大的数据分析平台,以及阿里云上的 QuickBI。
bi-designer 目前没有开源,因此文中使用的私有 npm 源
@alife/bi-designer
是无法在公网访问的。
本文介绍 bi-designer 设计器的使用 API。
bi-designer 设计有如下几个特点:
心智统一:编辑模式与渲染模式统一。
通用搭建:支持接入任意通用 npm 组件。
低入侵:围绕数据分析能力做了增强,但对组件代码无入侵。
渲染画布
做搭建,第一步是将画布渲染出来,需要用到 Designer
与 Canvas
组件:
import { Designer, Canvas } from '@alife/bi-designer'
export () => (
<Designer>
<Canvas />
</Designer>
)
Designer
:数据容器,用于管理渲染引擎数据流。-
参数
defaultPageSchema
:页面 DSL 默认值。参数
defaultMode
:控制编辑渲染状态,edit
orrender
。
Canvas
:渲染画布的所有组件,会根据 DSL 结构将组件一一渲染出来。
编辑模式
编辑模式 = 渲染画布(编辑模式)+ 拓展一些自定义面板。
import { Designer, Canvas } from '@alife/bi-designer'
export () => (
<Designer defaultMode="edit">
<div>Header</div>
<Canvas />
<div>Footer</div>
</Designer>
)
编辑模式的拓展采用了 JSX 模式,没有增加任何新的语法,只要放置任意数量的组件,并将画布 Canvas
摆放在想要的位置即可。
defaultMode
描述了当前引擎所处状态,有 edit
与 render
两个可选值,可以通过 { mode } = useDesigner(modeSelector)
获取。bi-designer 没有对 mode
做任何特殊处理,我们可以在 panel、组件中判断不同的 mode
走不同的逻辑,以此区分编辑与渲染态。
页面 DSL 结构
pageSchema
描述了页面 DSL 信息,其结构是一个 Map<组件 id, 组件实例信息>
。
这里统一一下名词:
组件实例信息:
componentInstance
。组件元信息:
componentMeta
。
那么 pageSchema
的结构大致如下:
{
"componentInstances": {
"1": {
"id": "1",
"componentName": "root",
},
"2": {
"id": "2",
"parentId": "1",
"componentName": "button",
}
}
}
根据 id
parentId
关系描述了组件父子关系,对于同一个父节点在流式布局下的顺序,还会增加 index
标记顺序。
注册组件
DSL 描述信息中最重要的是 componentName
,为了告诉渲染引擎这个组件是什么,我们需要将组件元信息(componentMetas
)传递给 Designer
:
import { Designer, Canvas, Interfaces } from '@alife/bi-designer'
export () => (
<Designer componentMetas={componentMetas}>
<Canvas />
</Designer>
)
const componentMetas: Interfaces.ComponentMetas = {
button: {
componentName: 'button',
element: Button
}
}
关于 componentMeta
会在下一篇精读详细介绍,这里只说明两个最重要的属性:
componentName
:组件名,唯一。element
:组件 UI 对象,对应一个 React 组件实例。
注意这里就留下了不少拓展空间,componentMetas
可以存储在服务端,element
可以远程异步加载,也可以在项目代码中固化,但传递给渲染引擎的 API 是固定的。
布局
bi-designer 支持流式布局、磁贴布局、自由布局三种模式,通过 Designer.layout
属性定义:
import { Designer, Canvas, Interfaces } from '@alife/bi-designer'
import { LayoutMover } from '@alife/bi-designer-stream-layout'
export () => (
<Designer layout={LayoutMover}>
<Canvas />
</Designer>
)
我们提供了三种不同的布局包,切换对应的包即可切换布局,你甚至可以再包裹一层,通过代码控制在运行时切换布局。
layout
会包裹在每个组件外层,无论是流式、磁贴还是自由布局,都可以通过附着在每个组件外层来实现。
操作/获取画布内容
只要在数据容器 Designer
下,就可以通过 useDesigner()
获取画布信息或者修改画布内容。
举个例子,比如实现组件配置面板,需要获取到 当前选中组件,以及实现操作 更新 DSL 中某个组件信息:
import { Designer, Canvas, useDesigner, selectedComponentsSelector } from '@alife/bi-designer';
const EditPanel = () => {
const { updateComponentById, selectedComponents } =
useDesigner(selectedComponentsSelector());
// 在合适的时候调用 updateComponentById 更新 selectedComponents
// 渲染组件配置表单..
}
export () => (
<Designer>
<Canvas />
<EditPanel />
</Designer>
)
我们在 Canvas
下面渲染了一个自定义组件 EditPanel
作为组件配置面板,这个配置面板中,最重要的是这块代码:
import { useDesigner, selectedComponentsSelector } from '@alife/bi-designer';
const { updateComponentById, selectedComponents } =
useDesigner(selectedC