上一篇《收集与渲染》文章中,我们提到收集与渲染的拆分设计,对后续的业务拓展会更方便。
本文将介绍渲染插件的设计,渲染插件可用于各种新特性的拓展绘制。
# 支撑业务的快速发展
我们设计了渲染引擎,只能满足基础图形的收集和绘制,包括文本、线段、矩形、图像等。
而应用到具体的业务中,则是使用这些基础图形来绘制出业务相关的内容。因此,我们可以考虑将基础的能力进行封装,提供更便利的能力给到业务侧使用。
# 分层封装底层能力
举个例子,依然是表格的场景,由于大多数内容都是以单元格为基本单位来进行绘制的,我们则可以封装出一个提供按单元格绘制的中间层能力。
而当业务侧进行编辑操作时,更新的范围除了单个格子,也会包括整行、整列、整表、所选区域等情况,因此我们可以封装给到业务侧这些能力。
到这里,渲染引擎架构如图:
这种分层能力不仅在渲染引擎中可以用到,即使在我们平时的页面开发中,也完全可以用到。最常见的包括将页面布局做分层拆分,然后进行渲染。
不过现在基本上渲染的流程和实现都交由前端框架来负责,而 DOM 的布局则都交给浏览器本身的排版引擎去处理,用 Canvas 来绘制布局的场景的确很少。
# 业务侧插件的设计
除了给上层业务提供封装好的能力,业务侧可以指定单元格范围进行重新渲染以外,还需要考虑另外一种的业务拓展场景:业务需要在单元格内绘制自己的内容,比如单元格背景高亮、一些特殊的图形属性单元格、图片绘制等等。
所以我们还需要给业务提供控制单元格绘制内容的能力。
前面一篇文章我们提到,每个单元格的绘制会有堆叠顺序,比如先绘制背景色,再绘制文字、边框线等等。那么,如果我们要给业务侧提供绘制的能力,他们同样需要可控制的堆叠顺序,和绘制内容的控制。
既然我们将渲染过程分成了收集和渲染两部分,渲染器的能力可以说是通用的能力,因为不管是单元格本身的绘制,还是业务侧新增的绘制内容,都离不开最基本的文字绘制、线段绘制、图形绘制、图像绘制等能力。
因此,我们可以考虑在收集过程中,通过提供插件的能力,让业务侧把想要绘制的内容收集起来。
插件提供的能力包括:
- 定义收集时机,比如指定单元格绘制时进行收集,或是按行、按列、全表绘制时进行收集。
- 定义绘制配置,比如指定单元格绘制时,阻断正常单元格绘制内容。举个例子,考虑某个单元格不需要正常渲染文字内容,而是需要渲染特定的图形内容(如评星、进度条等),则可以通过配置控制。
- 添加绘制内容到收集器,绘制内容可指定绘制类型(文字、线段、图形等)以及堆叠顺序。
插件的收集流程大概如图:
简单来说,插件的实现可能是:
// 该代码只是写了个思路,不作为最终的实现方式
abstract class BaseCollectPlugin {
protected collectWhen: 'column' | 'row' | 'cell' | 'sheet';
protected collectConfig: {
collectText: () => boolean;
collectBorder: () => boolean;
collectBackground: () => boolean;
}
protected collect: () => ICollectInfo;
protected constructor(dependency) {
this.collectWhen = dependency.collectWhen;
this.collectConfig = dependency.collectConfig;
this.collect = dependency.collect;
}
}
通过这样的方式,业务可以自由地控制某些范围内单元格的绘制内容,且不需要侵入性地修改核心的绘制流程,拓展性得到了很好的提升。
现在越来越多的软件都支持通过插件的方式来拓展能力,也允许开发者一起来打造插件体系。对于插件的设计来说,独立性、安全性、拓展性都是比较重要的考虑方向。
# 结束语
本文结合收集和渲染的渲染架构,设计了一套方便业务拓展的底层能力,包括提供支持可选范围的重新渲染能力,以及控制单元格绘制内容的插件能力。
很多时候我们都关注核心的架构能力,而往往忽略了业务的快速发展和迭代。实际上,架构就是为了不断变化的业务服务的,因此架构设计的时候,保留符合业务发展需要的拓展能力也是十分必要的。