文章目录
  1. 1. 渲染引擎完整的数据流向
    1. 1.1. 渲染引擎为什么需要计算
      1. 1.1.1. 1. 分行/换行计算(计算范围:格子)
      2. 1.1.2. 2. 行高计算(计算范围:整行)
      3. 1.1.3. 3. 覆盖格/隐藏格计算(计算范围:整行)
      4. 1.1.4. 4. 边框线计算(计算范围:整行)
  2. 2. 计算过程优化
    1. 2.1. 将计算任务做拆分
    2. 2.2. 预计算/异步计算
  3. 3. 结束语

前面《收集与渲染》一文中,我们简单提到说在一些复杂场景下,从服务端获取的数据还需要进行计算,比如依赖 Web 浏览器的计算,亦或是游戏引擎中的碰撞检测。

本文我们详细针对复杂计算的场景来考虑渲染引擎的优化。

渲染引擎完整的数据流向

对于需要进行较复杂计算的渲染场景,结合收集和渲染的架构设计,我们完整的渲染流程大概应该是这样的:

可见,完整的渲染流程里,计算的复杂程度会直接影响渲染是否及时,最终影响到用户的交互体验。在这里,我们还是以在线表格为例子,详细介绍下为什么有如此大的计算任务。

渲染引擎为什么需要计算

在表格中,画布绘制所需的数据,并不能完全从数据层中获取得到。对于以下一些情况,需要经过渲染引擎的计算处理才能正确绘制到画布上,包括:

1. 分行/换行计算(计算范围:格子)

如下图,当单元格设置了自动换行,当格子内容超过一行会被自动换到下一行。由于内容宽度的测量依赖浏览器环境,因此也是需要在渲染引擎进行计算的:

2. 行高计算(计算范围:整行)

当某个行没有设置固定的行高时,该行内容的高度可能会存在被自动换行的单元格撑高的情况,因此真实渲染的行高也需要根据分行/换行结果进行计算。

3. 覆盖格/隐藏格计算(计算范围:整行)

如下图,在没有设置自动换行的情况下,当单元格内容超出当前格子,会根据对齐的方向、该方向上的格子是否有内容,向对应的方向拓展内容,呈现向左右两边覆盖的情况:

4. 边框线计算(计算范围:整行)

受覆盖格影响,覆盖格和隐藏格(即被覆盖的格子)间的边框线会被超出的内容遮挡,因此对应的边框线也会受影响。

以调整列宽为例子,该操作涉及的计算包括:

可见,除了分行计算只涉及该列格子,一次列宽操作几乎涉及全表内容的计算,在大表下可能会导致几秒的卡顿,在一些低性能的机器上甚至会达到十几秒。由于该过程为同步计算,网页会表现为无响应,甚至严重的情况下会弹窗提示。

计算过程优化

之前我在《前端性能优化–卡顿篇》一文中,有详细介绍对于大任务计算的优化方向,包括:

  1. 赋值和取值的优化。
  2. 优化计算性能和内存,包括使用享元的方式来优化数据存储,减少内存占用
    及时地清理不用的资源,避免内存泄露等问题。
  3. 计算大任务进行拆解。
  4. 引入其他技术来加快计算过程,比如 Web Worker、WebAssembly、AOT 技术等。

对于较大型的前端应用,即使并非使用 Canvas 自行排版,依然可能会面临计算耗时过大的计算任务。当然,更合理的方式是将这些计算放在后台进行,直接将计算完的结果给到前端使用。

也有一些场景,尤其是前端与用户交互很重的情况下,比如游戏和重编辑的产品。这类产品无法将计算任务放置在后端,甚至无法将计算任务拆分到 Web Worker 进行计算,因为请求的等待耗时、Worker 的通信耗时都会影响用户的体验。

对该类产品,最简单又实用的方法便是:拆。

将计算任务做拆分

将计算任务做拆分,我们可以结合计算场景做分析,比如:

  • 只加载和计算最少的资源,比如首屏的数据
  • 只进行可视范围内的计算和渲染更新,在可视区域外的则做异步计算或是暂不计算
  • 支持增量计算和渲染,即仅变更的局部内容的重新计算和渲染
  • 支持降级计算,对计算任务做优先级拆分,在用户机器性能差的情况下考虑降级渲染,根据优先顺序先后计算和绘制
  • 设计任务调度器,对计算任务做拆分,并设计优先级进行调度

比如,React16 中新增了调度器(Scheduler),调度器能够把可中断的任务切片处理,能够调整优先级,重置并复用任务。

调度器会根据任务的优先级去分配各自的过期时间,在过期时间之前按照优先级执行任务,可以在不影响用户体验的情况下去进行计算和更新。

通过这样的方式,React 可在浏览器空闲的时候进行调度并执行任务。

预计算/异步计算

还有一种同样常见的方式,便是将计算任务进行拆分后,通过预判用户行为,提前执行将用到的计算任务。

举个例子,当前屏幕内的数据都已计算和渲染完毕,页面加载处于空闲时,可以提前将下一屏幕的资源获取,并进行计算。

这种预计算和渲染的方式,有些场景下也会称之为离屏渲染。离屏渲染同样可以作用于 Canvas 绘制过程,比如使用两个 Canvas 进行交替绘制,或是使用 worker 以及浏览器提供的 OffscreenCanvas API,提前将要渲染的内容计算并渲染好,等用户进入下一屏的时候可以直接拿来使用。

如果是页面滚动的场景,还可以考虑复用滚动过程中重复的部分内容,来节省待计算和渲染的任务数量。

这些方案,我们后面都会详细进行一一讨论,本文就不过多描述了。

结束语

或许很多开发同学都会觉得,以前没有接触过大型的前端项目,或是重交互重计算的产品,如果遇到了自己不知道该怎么做优化。

实际上,大多数的优化思路都是相似的,但是我们需要尝试跨越模板,将其应用在不同的场景下,你就会发现能得到许多想象以外的优化效果。

纸上得来终觉浅,绝知此事要躬行。不要自己把自己局限住了哟~

码生艰难,写文不易,给我家猪囤点猫粮了喵~

B站: 被删

查看Github有更多内容噢:https://github.com/godbasin
更欢迎来被删的前端游乐场边撸猫边学前端噢

如果你想要关注日常生活中的我,欢迎关注“牧羊的猪”公众号噢

作者:被删

出处:https://godbasin.github.io

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

文章目录
  1. 1. 渲染引擎完整的数据流向
    1. 1.1. 渲染引擎为什么需要计算
      1. 1.1.1. 1. 分行/换行计算(计算范围:格子)
      2. 1.1.2. 2. 行高计算(计算范围:整行)
      3. 1.1.3. 3. 覆盖格/隐藏格计算(计算范围:整行)
      4. 1.1.4. 4. 边框线计算(计算范围:整行)
  2. 2. 计算过程优化
    1. 2.1. 将计算任务做拆分
    2. 2.2. 预计算/异步计算
  3. 3. 结束语