精读《如何抽象可视化搭建》

在做任何可视化搭建项目时,第一步都要思考如何抽象。

如果不抽象,当搭建项目做到后期可能会出现 API 杂乱,难以维护的问题;做到一半甚至会怀疑为什么需要一个搭建框架,怀疑把框架去掉会不会效率更高;在后期发现不能自然的水平拓展到仪表盘、大屏、表单搭建场景等。

所以如果在维护一套可视化搭建系统时,不管这个系统的上层是 BI、大屏、表单填报,还是脑图也好,无论是什么,都要先思考一下这些系统背后的底层是什么,需不需要抽象,抽象的意义和价值在哪。

以下结合笔者的经验,尝试给出一种思考角度。

精读

什么是可视化搭建

表单搭建、中后台应用搭建、BI 仪表盘搭建、大屏搭建都算可视化搭建,因为它们都是在一个画布上拖拖拽拽完成的。

那么组件配置表单算搭建吗?聚焦单组件分析的可视化探索呢?幻灯片呢?

比如组件配置表单,它基于 UI 组件树抽象的话,就是可视化搭建,但如果基于表单结构抽象,就是 JsonSchema,但真的所有业务场景都是数据完全映射 UI 吗?不一定,因为 UI 可以为了用户操作方便而加入更多辅助元素,甚至把一个属性拆成多个 UI 填写,所以基于可视化搭建,也就是 UI 组件树抽象的一定可以覆盖所有表单场景,但不一定是描述效率最高的方式。

如果每种可视化搭建场景都定义一套协议与实现,那按照搭建平台的复杂度,想同时维护两个类搭建平台的成本一定是两倍,而且不同维护人员很难交流。又或者某些可以按照搭建思路解决的场景,因为实现时经验不足,没有进行抽象,甚至进行了另一套定制抽象,回过头来看可能积重难返,团队不得不接受多套笨重实现的现状。

所以建议将这些场景都视为可视化搭建场景,用一套接口描述结构、API 方法,让看似百花齐放的编辑器之下拥有统一的上下文与实现。

可视化搭建的分层

对于不同种类的可视化搭建平台,我们尝试寻找其分层设计的最大公约数。如果把可视化搭建底层设定为逻辑层,即这个层是 UI 无关的,仅关心组件树结构、逻辑功能,那么对于每种平台的分层应该是这样的:

  1. 表单搭建:逻辑层、表单联动协议层、表单控件、业务层。
  2. 中后台应用搭建:逻辑层、应用联动协议层、应用控件、业务层。
  3. BI 仪表盘:逻辑层、筛选联动协议层、可视化控件、业务层。
  4. 大屏搭建:逻辑层、画布编辑控制器层、可视化控件和基础图形控件、业务层。

最底层的逻辑层应该可以统一所有类型搭建系统,并成为开发人员统一上下文的。它可以包含以下基础能力:

  1. 定义组件树结构。
  2. 定义组件元信息。
  3. 按照组件树结构递归渲染画布。
  4. 支持布局、取数、联动、筛选、校验等一系列拓展能力,业务可根据需要定制。
  5. 提供所有业务层都需要的能力,比如性能优化的组件冻结、状态管理、对组件树增删改查的 API。

在逻辑层完备后,再开发上层应用就会轻松很多,只要注册组件、根据业务需要在组件树初始化或组件初始化,或组件元信息注册时添加定制逻辑,与系统功能对接,并补充业务特色的如自定义布局能力,这样就可以用简单的三言两语说清楚整个系统是如何设计的。

逻辑层存在的必要性

再回到问题的根源:对逻辑层做统一的抽象到底是不是多余的?

要回答这个问题,需要先了解我们手头里有哪些工具:基础开发工具 html、js、css,并且 html 也提供了一套标准化的 xml 结构;vue、react 等开发框架,基础组件、应用生命周期与事件定义。理论上基于这些,我们就可以直接上手写一个可视化搭建平台了,似乎也可以不抽象。但真正要上手时,一定会遇到以下几个通用问题需要处理:

定义组件树结构

无论做表单搭建、报表搭建、大屏搭建还是脑图画布,第一个想到的肯定是如何描述这个画布结构,而无论画布是横着排还是竖着排,横竖都是一棵树。HTML 树不能直接搬过来,一是 HTML 树的完整结构太大而我们需要的更精简的结构,二是业务层框架一般都先有一套虚拟树再转化为 dom 树,因果关系也没法反过来。而这棵树也完全可以做最大程度的抽象,即定义组件 ID、组件名、属性(Props)、子节点。

定义对组件树增删改查函数

有了组件树肯定需要对其进行增删改查操作,因为无法基于 document API,上层框架如 vue、react 也不提供对任何标准组件树的增删改查 API,这部分能力势必要手动实现。

生命周期

假设完全依赖 React 框架提供的组件生命周期,是可以完成大部分业务逻辑,但这意味着定义不够精细化。比方说,我们在组件 Mount 的实际监听了联动、实现取数、设置冻结等等效果,虽然也可以实现,但会遇到要不要抽象的问题:

  • 如果不抽象,业务代码就会乱糟糟的,比较难读。
  • 如果抽象,就要把联动、取数、冻结等等模块归类,封装成函数,甚至可以提供主动调用机制,UI 与逻辑解耦,但当业务层精细的去做这件事就会发现,这就是在做框架层的抽象工作,所以还不如一开始就把这些生命周期抽象到框架里。

逻辑层有两个核心结构,第一个是组件树结构,包含了对每个组件实例的定义;第二个是组件元信息结构,包含了对每个组件的元信息描述,大概如下图所示:

逻辑层的难点就是在元信息定义足够多、足够通用的生命周期回调函数,并且这些回调函数还能尽可能的功能正交。

组件渲染

通常一棵树按照 json 结构描述自顶向下自动渲染就可以了,但也有一些时候,比如内嵌一个富文本组件,而富文本内又嵌入一些画布组件,这些组件需要像普通画布组件一样可交互,此时就有 渲染一个不存在于组件树的组件实例 的需求,而这样的动态组件又要无感知的满足上面所说的各类生命周期,这也是不小的工作量。

功能的拓展抽象

等可视化搭建平台正式维护时,就至少会遇到组件版本升级、不同类型的布局方案对接、三方组件注册等需求,这些功能如何加入到现有的搭建平台,而不让其他功能感知,是需要精心设计的。如果逻辑层把这一点抽象好,在每个功能设计一个钩子,实现一个功能时无需感知其他功能,那平台的功能拓展就会保持一个恒定的速度,不随功能增加而变得难以维护。

可见,可视化搭建不断迭代的过程就是自身不断抽象的过程,逻辑层实现的好坏直接影响到后期的维护性与拓展性,所以好好设计逻辑层可以让开发事半功倍。

组件配置表单要不要用搭建方案做

组件配置直接用表单方案而不是搭建,似乎是最容易想到的。但当每个组件都要自定义配置,我们就不得不选择基于 JsonSchema 描述的表单方案,但这与搭建应用本身的技术栈割裂了,随着联动功能的要求越来越多,会越来越发现小小的表单渲染引擎维护得越来越复杂,甚至复杂度与画布不分上下,此时再叹息两边技术栈不统一就已经晚了。

换个角度想一下,搭建应用不也要考虑组件间联动吗?从表单值能力来看,搭建场景并不要求每个组件都拥有一个值,反倒是可以将组件任意 props 属性看作表单值更具有 “弹性”,我们可以拓展任意 Key 作为表单值。

另外,从数据结构触发来描述表单看似很美好,但当表单变得越来越复杂,UI 越来越定制后,势必引入新的 UI 节点或者新的结构描述,与其后期拓展到一个不纯净的 JsonSchema 结构,不如一开始就放弃这个幻想,用 UI 组件树结构描述表单,这样事情就变得简单了:“先描述组件树,再定义每个节点分别用什么组件渲染,响应表单的哪部分 Key”。

总结

总结一下,回到主题,抽象可视化搭建的方法是分层:以逻辑层打底,提供一套标准规范与 API 接口,上层注册组件、实现布局,一切围绕着标准化的逻辑层进行拓展。

而可视化搭建的每一层都可以分别写单元测试,保证最终变化的代码只有业务层的对接部分,应用的稳定性就提高了。

最后提一个思考题:你是觉得可视化搭建应该如何抽象?如果想要做到每一层独立正交,你会如何设计 API 呢?

讨论地址是:精读《如何抽象可视化搭建》· Issue #463 · dt-fe/weekly

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

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容