当前期刊数: 280
接下来用实战来说明该可视化搭建框架是否好用,以下几条原则需要始终贯穿在下面每个实战场景中:
- 复杂的业务场景,背后使用的框架 API 是简单的。
- 底层 API 并不为业务场景特殊编写,而是具有很强的抽象性,很容易挖掘出其他业务场景的用法。
- 所有场景都是基于有限的几条基础规则实现,即背后实现的复杂度不随着业务场景复杂度提升而提升。
上卷下钻
上卷下钻其实是 组件作用于自身的筛选。
所以上卷下钻背后的实现原理应该与筛选、联动一样。利用 setValue
在点击下钻按钮时,修改组件自己的 value
,然后通过 valueRelates
让该组件的联动作用于自身,剩下的逻辑就和普通筛选、联动没有太多区别了,区别仅仅是联动触发源是自己:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { ComponentMeta } from "designer";
const chart: ComponentMeta = { componentName: "chart", element: Chart, runtimeProps: ({ selector, setValue, componentId }) => ({ value: selector(({ value }) => value), onChange: (value: string) => setValue(componentId, value), }), valueRelates: ({ componentId }) => [ { sourceComponentId: componentId, targetComponentId: componentId, }, ], fetcher: ({ selector }) => { const relates = selector(({ relates }) => relates); }, };
|
上卷下钻就是作用于自身的联动。
Tabs 组件
利用组件树解析规则,我们任意找一个 Key 存放每个 TabPanel 的子元素就可以了。
我们利用 props.tabs
存放 tabs 配置,props.content
存放每项 TabPanel 的子组件,因为其顺序永远和 props.tabs
保持一致,我们可以简单的使用下标匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const tabs = { componentName: "tabs", element: TabsComponent, defaultProps: { tabs: [ { title: "tab1", key: "1", }, ], content: [ { componentName: "gridLayout", }, ], }, };
|
而 TabsComponent 组件实现就完全与平台解耦了,即使用 props.tabs
与 props.content
渲染即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const TabsComponent = ({ content, handleAddTab, handleDeleteTab, tabs }) => ( <Tabs editable defaultActiveTab="1" onAddTab={handleAddTab} onDeleteTab={handleDeleteTab} > {tabs.map((tab, index) => ( <TabPane key={tab.key} title={tab.title}> {content[index]} </TabPane> ))} </Tabs> );
|
tabs 使用 treeLike 结构,按照下标存储组件实例。
富文本内嵌组件实例
与 tabs 很像,区别是富文本内嵌入的组件实例数量是不固定的,每一个组件实例都对应富文本某个 block id. 下面是富文本实现代码的一部分:
1 2 3 4 5 6 7 8 9 10 11 12
| const SomeRichTextLibrary = (props) => { const RenderCustomBlock = useCallback( (blockId: string) => { return props.blockElements.find( (componentInstance) => componentInstance.componentId === blockId ); }, [props.blockElements] ); };
|
富文本一般拥有自定义 block 区块的能力,我们只要将 block id 与组件实例 id 绑定,然后将组件实例存储在 props.blockElements
,就可以轻松匹配到对应组件实例了。
其中 props.blockElements
的结构如下:
1 2 3 4 5 6 7 8 9 10 11 12
| { "blockElements": [ { "componentId": "block1", "componentName": "chart" }, { "componentId": "block2", "componentName": "radar" } ] }
|
富文本的结构可能如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "type": "rich_text", "content": [ { "type": "paragraph", "text": "This is a paragraph of rich text." }, { "type": "heading", "level": 2, "text": "This is a heading" }, { "type": "block", "blockId": "block1" }, { "type": "block", "blockId": "block2" } ] }
|
最后两个 block 是自定义区块,通过自定义 RenderCustomBlock
来渲染,我们正好可以通过 blockId
对应到 componentId
,在 props.blockElements
中找到。
富文本的实现思路和 tabs 基本一样,只是查找组件实例的逻辑不同。
实现任意协议
我们也许为了进一步抽象,或对指定业务场景降低配置门槛,在组件树拓展一些额外的 json 结构协议做一些特定功能。
以拓展事件配置为例,假如我们需要实现如下协议:每个组件实例信息上拓展了 events
属性,通过配置这个属性可以实现一些内置动作,如打开 Modal。这个协议至少要定义触发源是什么 trigge
r、做什么事情 type
以及作用的目标组件 targetId
:
1 2 3 4 5 6 7 8 9 10
| { "componentName": "button", "events": [ { "trigger": "onClick", "type": "openModal", "targetId": "123" } ] }
|
如上面的例子,只要定义好触发源、类型和目标组件,就可以在按钮组件 onClick
时将目标组件 visible
设为 true
,实现弹出 Modal 的效果。
实现思路是,利用 onReadComponentMeta
,在所有组件的元信息做拓展。比如要拓展这种事件,一般 Trigger 都要绑定在组件 Props 的回调上(如果是全局监听,可以绑定在全局并利用事件机制通信给组件),那就可以通过 runtimeProps
进行绑定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const App = () => ( <Designer onReadComponentMeta={(meta) => ({ ...meta, runtimeProps: (options) => { const result = meta.runtimeProps?.(options) ?? {}; const events = options.selector( ({ componentInstance }) => componentInstance.events ); events?.forEach((event) => { switch (event.type) { case "openModal": // 给组件添加新的 trigger 绑定 result[event.trigger] = options.setRuntimeProps( event.targetId, (props) => ({ ...props, visible: true, }) ); break; } }); return result; }, })} /> );
|
除此之外,我们还可以想象有更多的协议可以通过这种方式处理响应,无论何种协议,背后都是基于组件元信息的实现,易懂且单测有保障。
总结
本文我们总结了三个场景实战:
- 利用 treeLike 结构在组件内渲染任意数量的子组件实例,如 tabs 或富文本。
- 利用组件联动的 API,实现筛选、联动以及上卷下钻。
- 利用 onReadComponentMeta 为所有组件元信息统一增加逻辑,用来解读如
props
属性中定义的某些规则,进而实现任意协议。
讨论地址是:精读《可视化搭建 - 场景实战》· Issue ##485 · dt-fe/weekly
如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)