# 组件渲染
当组件初始化、修改属性、改变大小、修改过滤参数等操作时,都会触发组件渲染。
下图描述了用户、页面和组件之间的核心交互逻辑。
# 用户、页面和组件的交互行为
- 设计器选中组件显示属性面板
用户选中组件时设计器会显示组件的属性面板。系统根据capabilities.json定义的属性和属性面板配置,显示当前选中组件的属性。
- 设计器通过属性面板修改组件属性
用户通过属性面板修改组件属性后,系统会触发组件的render
函数渲染组件。组件渲染时通过componentBuilder
获取组件的属性。
- 设计器预览
当仪表板
和SuperPage
这种所见即所得的模块在设计器中切换预览时,不会重新渲染组件,由于查看区域的视窗大小变化,会触发组件的render
重新调整组件大小。
- 改变页面大小
用户改变了页面的视窗大小后,页面会从画布开始逐层向下分发大小变化事件,通知每一个组件render
。
- 修改过滤参数
当改变了模型过滤条件引用的参数自动过滤的组件值后,使用模型查询数据的组件,需要重新发起查询。页面会通知这些组件数据变化,如果组件是显示
的,那么先发起查询,查询完成后通知组件render
。
# IVisualComponent
所有的扩展组件开发都需要实现IVisualComponent
接口。实现类的名字和package.json
中配置的visualClassName
一致。
页面在初始化IVisualComponent
时,会将其包装为一个BOComponentWrapper
,BOComponentWrapper
提供渲染组件位置、大小、标题、背景等通用能力,并创建一个包含vc-body
class的div
作为domParent
传递给IVisualComponent
的render
函数作为IVisualComponent
渲染的根DOM。
export class MyVisualComponent implements IVisualComponent {
constructor(args: VisualComponentArgs) {
//初始化创建组件时调用一次,初始化组件的基本信息
}
public render(args: VisualComponentRenderArgs): void | Promise<void> {
//渲染组件,创建DOM结构,输出样式,绑定事件...
}
}
# constructor
组件初始化时调用constructor
。在构造函数中可以完成一些组件的初始化操作。
# VisualComponentArgs
visualObject
,可视化页面对象,包含了一些通用服务compBuilder
,组件的属性和对象访问接口,可以获取到组件元数据,下级组件等
# render
组件初始化、数据变化、主题变化等操作时都会调用render
函数。
# VisualComponentRenderArgs
domParent
,组件的父DOM,组件的DOM结构应该都创建在该DOM内。renderType
,组件渲染类型,渲染时判断类型决定如何增量渲染。viewPort
,组件渲染的大小和位置信息。dataViews
,组件的数据对象,通过package.json
的dataViewMappings
配置的查询产生的查询结果。viewMode
,页面的查看模式,包括编辑、预览、查看。modifiedInfo
,组件的增量变化信息。组件在渲染时可以使用该信息进行增量渲染,不用每次都重新渲染整个组件。例如改变了组件的背景,传入的组件。
# updateType
名称 | 值 | 描述 |
---|---|---|
None | 0 | 无变化 |
Data | 1 | 数据变化,dataViews 和modifiedInfo 中包含了变化的信息 |
Theme | 2 | 主题变化 |
ViewMode | 4 | 切换预览 |
Resize | 8 | 尺寸变化 |
Visible | 16 | 显示隐藏变化 |
All | 128 | 全量刷新 |
# modifiedInfo
# dispose
销毁组件,组件被删除时调用。默认组件会将IVisualComponent
移除出domParent
,如有额外的销毁逻辑,可实现该函数。
# 增量渲染组件大小变化
当组件所在父容器尺寸变化时(比如用户拖动了浏览器窗口的大小),会触发组件渲染,renderType
为ComponentRenderType.Resize
。
如果组件本身就是自适应的,组件的尺寸变化时内部的DOM元素会自动重新布局,那么可以不处理该增量变化。
如果组件内部的布局需要由程序进行控制,比如用canvas绘制的图形,当组件尺寸变化时,canvas需要重新绘制,那么需要实现此函数。实现者可以简单的重新调用一下render
函数,实现全量渲染,也可以按需自己实现更精细化的增量渲染机制。
export class MyVisualComponent implements IVisualComponent {
public render(args: VisualComponentRenderArgs): void {
let renderType = args.renderType;
if (renderType & ComponentRenderType.Resize) {
this.doResize();
}
}
private doResize(): void {
this.charts.resize();
}
}
# 弹出组件
弹出组件在没有弹出时不输出DOM,通过调用组件方法显示和隐藏组件。
弹出组件在隐藏时应该不响应任何事件,当显示时才一次性更新组件为最新状态。
在设计器中,弹出组件显示成一个带名称的方块,双击编辑后显示一个占据整个画布的编辑区。
TODO 截图
以菜单组件为例。通过调用组件方法交互,设置show
显示菜单,hide
隐藏菜单。
{
"name": "MyMenu",
"popupComponent": true,
"accessableMethods": [{
"name": "show"
}, {
"name": "hide"
}]
}
export class MyMenu implements IVisualComponent {
private visible: boolean;
private menu: Menu;
public render(args: VisualComponentRenderArgs): void {
if(!visible || !this.menu) {
return;
}
//render menu when visible
}
public show(args: BOInvokeComponentMethodArgs): void {
this.visible = true;
let menu = this.menu;
if (!menu) {
menu = this.menu = new Menu();
}
//init items from data
let items;
menu.setItems(items);
menu.showAt(event);
}
public hide(args: BOInvokeComponentMethodArgs): void {
this.visible = false;
this.menu?.hide();
}
}
# 逻辑组件
逻辑组件不输出任何DOM元素,在render
中执行程序逻辑,通常用于定时器等场景。
逻辑组件在初始化或数据变化时才调用render
函数,不响应其它类型事件。
以定时器组件为例,页面全部加载完毕后开始计时,可以通过组件方法start
和stop
开始和停止计时。
{
"name": "MyTimer",
"logicComponent": true,
"accessableMethods": [{
"name": "start"
}, {
"name": "stop"
}]
}
export class MyTimer implements IVisualComponent {
private stopped: boolean;
public render(args: VisualComponentRenderArgs): void {
this.start();
}
public start(args: BOInvokeComponentMethodArgs): void {
this.stopped = false;
// start timer
}
public stop(args: BOInvokeComponentMethodArgs): void {
this.stopped = true;
}
}