接下来用实战来阐明该可视化搭建框架是否好用,以下几条准则须要始终贯通在上面每个实战场景中:

  1. 简单的业务场景,背地应用的框架 API 是简略的。
  2. 底层 API 并不为业务场景非凡编写,而是具备很强的抽象性,很容易挖掘出其余业务场景的用法。
  3. 所有场景都是基于无限的几条根底规定实现,即背地实现的复杂度不随着业务场景复杂度晋升而晋升。

上卷下钻

上卷下钻其实是 组件作用于本身的筛选

所以上卷下钻背地的实现原理应该与筛选、联动一样。利用 setValue 在点击下钻按钮时,批改组件本人的 value,而后通过 valueRelates 让该组件的联动作用于本身,剩下的逻辑就和一般筛选、联动没有太多区别了,区别仅仅是联动触发源是本人:

import { ComponentMeta } from "designer";const chart: ComponentMeta = {  componentName: "chart",  element: Chart,  // 利用 runtimeProps 将组件 value 映射到 props.value,将 props.onChange 映射为 setValue 批改本身 value  runtimeProps: ({ selector, setValue, componentId }) => ({    value: selector(({ value }) => value),    onChange: (value: string) => setValue(componentId, value),  }),  // 本人联动本人  valueRelates: ({ componentId }) => [    {      sourceComponentId: componentId,      targetComponentId: componentId,    },  ],  fetcher: ({ selector }) => {    // relates 可能来自本人、其余筛选器组件实例,或者其余图表组件实例    const relates = selector(({ relates }) => relates);    // 依据 relates 下钻 ...  },};

上卷下钻就是作用于本身的联动。

Tabs 组件

利用组件树解析规定,咱们任意找一个 Key 寄存每个 TabPanel 的子元素就能够了。

咱们利用 props.tabs 寄存 tabs 配置,props.content 寄存每项 TabPanel 的子组件,因为其程序永远和 props.tabs 保持一致,咱们能够简略的应用下标匹配。

const tabs = {  componentName: "tabs",  element: TabsComponent,  defaultProps: {    // 寄存 tabPanel 配置    tabs: [      {        title: "tab1",        key: "1",      },    ],    // 寄存每个 tabPanel 内子画布的组件实例    content: [      {        componentName: "gridLayout",      },    ],  },};

而 TabsComponent 组件实现就齐全与平台解耦了,即应用 props.tabsprops.content 渲染即可:

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. 上面是富文本实现代码的一部分:

const SomeRichTextLibrary = (props) => {  // 自定义渲染 block 槽位  const RenderCustomBlock = useCallback(    (blockId: string) => {      // 渲染组件实例      return props.blockElements.find(        (componentInstance) => componentInstance.componentId === blockId      );    },    [props.blockElements]  );};

富文本个别领有自定义 block 区块的能力,咱们只有将 block id 与组件实例 id 绑定,而后将组件实例存储在 props.blockElements,就能够轻松匹配到对应组件实例了。

其中 props.blockElements 的构造如下:

{  "blockElements": [    {      "componentId": "block1",      "componentName": "chart"    },    {      "componentId": "block2",      "componentName": "radar"    }  ]}

富文本的构造可能如下:

{  "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。这个协定至多要定义触发源是什么 trigger、做什么事件 type 以及作用的指标组件 targetId:

{  "componentName": "button",  "events": [    {      "trigger": "onClick",      "type": "openModal",      "targetId": "123"    }  ]}

如下面的例子,只有定义好触发源、类型和指标组件,就能够在按钮组件 onClick 时将指标组件 visible 设为 true,实现弹出 Modal 的成果。

实现思路是,利用 onReadComponentMeta,在所有组件的元信息做拓展。比方要拓展这种事件,个别 Trigger 都要绑定在组件 Props 的回调上(如果是全局监听,能够绑定在全局并利用事件机制通信给组件),那就能够通过 runtimeProps 进行绑定:

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;      },    })}  />);

除此之外,咱们还能够设想有更多的协定能够通过这种形式解决响应,无论何种协定,背地都是基于组件元信息的实现,易懂且单测有保障。

总结

本文咱们总结了三个场景实战:

  1. 利用 treeLike 构造在组件内渲染任意数量的子组件实例,如 tabs 或富文本。
  2. 利用组件联动的 API,实现筛选、联动以及上卷下钻。
  3. 利用 onReadComponentMeta 为所有组件元信息对立减少逻辑,用来解读如 props 属性中定义的某些规定,进而实现任意协定。
探讨地址是:精读《可视化搭建 - 场景实战》· Issue #485 · dt-fe/weekly

如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

版权申明:自在转载-非商用-非衍生-放弃署名(创意共享 3.0 许可证)