10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈
导语 |腾讯文档 SmartSheet 视图是多种视图中的一种,该模式下 FPS 仅 20 几帧(一般 Sheet 视图下 58 帧),用户体验十分卡顿。腾讯文档团队针对该问题进行优化,通过禁用取色、多卡片离屏烘托等办法完成 FPS 接近 60 帧,提升两倍多。本文将详细介绍其应战和解决方案,并输出通用的经验办法。希望本文对你有协助。

目录

1 前言

2 增量烘托

3 剖析火焰图

4 禁用取色

5 削减查找成果匹配

6防止运用 clone

7多卡片离屏烘托

7.1多卡片vs整屏

7.2 完成

8文本缓存

9 最终

01

前言

腾讯文档智能表格是一种具有多视图的新型表格。智能表格也是一个天然的低代码渠道,只需运用开放的增修正查 API 就能完成一个后台办理体系,利用提供的各种视图将数据展现出来。它本质上是一个在线数据库,具有更丰富的列类型和视图。智能表格能够让一份数据多种维度展现。目前现已有表格视图、看板视图(SmartSheet 视图)、画册视图、甘特视图、日历视图等。

除了最被熟知的表格视图之外,SmartSheet 看板视图以卡片的形式来展现,十分适合做一些运营活动和项目办理,从而开端得到重视。看板视图能够依据单选列作为分组依据,进行卡片的一个聚合分组展现。卡片的高度是不固定的,只要当时列有内容才会展现出来。下图是腾讯文档智能表格 SmartSheet 看板视图的无封面版本和有封面版本:

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

SmartSheet 看板视图上线后,10 w 单元格场景下的 FPS只要 20 多帧,比起Sheet 视图的 58 帧差距比较大,用户体验十分卡顿。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

FPS (Frames Per Second) 便是每秒钟画面的更新次数。理论上 FPS 越高,动画就会越流通。由于大多数设备屏幕刷新率都是 60 次 / 秒,所以一般来说 FPS 为 60 帧的时分最流通,此刻每帧的耗费时刻约为 16.67 ms。假如 FPS 低于 30 帧,就会呈现明显的卡顿和不流通。所以腾讯文档团队优化的重点目标是:尽量将每一帧的耗时降低到 16.67 ms。

02

增量烘托

Smart Sheet 看板是多种视图中的一种。它首要是多个分组来组成的,每个分组又包含了多个卡片。翻滚的时分包含左右分组翻滚、分组内卡片上下翻滚两种。

先来了解烘托层的完成,Smart Sheet 看板烘托层初始化分为4个阶段

  • 第一阶段,搜集核算文本宽高、切断等等;

  • 第二阶段,搜集各种树形结构的 widget,比方 textPainter、cardPainter、groupPainter 等等。

  • 第三阶段,依据 widget 进行制作,从根 layoutTree 开端递归子节点履行 painter 办法;

  • 第四阶段,Konva 履行 Layer 的 batchDraw 办法,递归履行子节点的 draw 办法。

10 w 单元格不会将全部卡片都给制作出来。由于它一方面会导致制作时刻过长,另一方面存放制作信息占用的内存太多。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

所以只会搜集可视区域内的 widget 进行制作。在翻滚的时分,管帐算出需求销毁的卡片和需求新增的卡片,然后开端销毁前面的节点,从头创立新的节点,进行增量烘托。对应上面的第 2、3 步,但此刻只会搜集增量的 Painter。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

03

剖析火焰图

首先需求知道翻滚的时分首要是耗时在哪里。翻开 Chrome 的 Performance 选项,挑选最左边的实心圆录制,在页面上用鼠标翻滚。最终生成了下面这份火焰图,能够看到有许多红色倒三角,阐明这里呈现了一些很耗时的操作。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

扩大这个火焰图,能够看到其间的一个 Task 的耗时,也便是一帧的耗时。能够看到两种状况,后者明显比前者耗时多太多了。

  • Task1:

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

  • Task2:

    10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

那翻滚的时分烘托层做了哪些作业呢?首要是下面几步:

  • 第一步,对原来的分组设置偏移量;

  • 第二步,核算新的可视区域,包含需求销毁、创立的分组和卡片;

  • 第三步,搜集分组或许卡片的 widget;

  • 第四步,依据 widget 进行制作,首要是创立 Konva 节点,增加子节点等;

  • 第五步,触发 Layer 的 batchDraw 办法,遍历子节点进行制作。

04

禁用取色

能够从上面看到 getImageData 耗时十分多,那为什么翻滚的时分会用到 getImageData 呢?这就不得不说到 Canvas 的事情体系了。

Canvas 不像 DOM 一样具有事情体系,所以无法直接知道当时点击的是哪个图形,需求开发者自己完成一套事情体系。简略来说,便是知道某个坐标点当时对应的是什么图形。

Konva 为了能够依据坐标点匹配到触发的元素,采用了**色值法——**也便是在内存里边的 hitCanvas 里边制作一模一样的图形,给这个图形加一个随机填充色,生成一个 colorKey。然后以这个 colorKey 作为 key,Shape 作为 value,存了起来。

事情触发时通过 hitCanvas 的 getImageData 办法拿到 colorKey,进一步拿到对应的 Shape。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

咱们在自己电脑本地履行了 1000 次 getImageData,发现耗时十分多。在翻滚的时分,很简单触发很多调用 getImageData。

Navigated to file xx
 getImageData 1000次: 250.051025390625 ms
 Navigated to file xx
 getImageData 1000次: 245.02587890625 ms
 Navigated to file xx
 getImageData 1000次: 245.637939453125 ms
 Navigated to file xx
 getImageData 1000次: 254.847900390625 ms

怎样防止调用 getImageData 呢?咱们来翻翻 Konva 的源码。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

翻滚的时分,触发的是 wheel 事情。只需求在翻滚的时分设置 layer 的 isListening 为 false 即可。等翻滚完毕后再设置回来,所以这里是 debounce 的逻辑。

05

削减查找成果匹配

前面咱们说过,烘托层在烘托的时分会进行搜集,在翻滚的时分由于或许会有查找成果高亮的存在,所以也要核算当时卡片是否匹配查找成果。假如匹配了,那就设置背景色。

但假如在没有启动查找的时分,不应该遍历 layoutTree,而是应该直接回来。提早回来,能够节省大约 2 ms 的查找高亮搜集时刻。

06

防止运用 clone

许多文本和矩形有共同特点,所以咱们原本是先创立了一个节点,运用的时分通过 clone 的办法复用,然后用 setAttrs 来设置新的 config。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

但 clone 的完成比较杂乱。能够理解成进行了一次深复制,会带来一些功能损耗。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

这里不够高雅,能够提早缓存通用的 config 值,然后直接运用 new 来创立节点。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

从图上能够看到,很明显耗时下降了。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

当咱们优化到这一步发现:在没有呈现新的卡片时,翻滚的耗时现已十分少了,基本上耗时都在制作阶段。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

制作阶段的耗时到达了 13 ms 之多。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈


07

多卡片离屏烘托

制作阶段要怎样去优化耗时呢?页面翻滚的时分,每次其实只移动了一小段距离,只要这部分是新增的。那也就意味着前面大部分都是不变的,仅仅增加了一些偏移量,假如能够对其进行复用,那必定能够大大削减耗时。

离屏烘托是 Canvas 的一种遍及的优化手段。比方腾讯文档团队的 Sheet 和 Word 都有离屏烘托,思路都是在翻滚的时分,通过 drawImage 来复用前面现已制作的部分,然后再制作增量的部分,这样能够削减很多文本的制作。

7.1 多卡片 vs 整屏

Smart Sheet 相比 Sheet 和 Word 来说会特别一些,腾讯文档团队运用了 Konva 这个结构,它本身封装了一套烘托逻辑,所以关于 Word 这种离屏烘托来说,完成起来比较费事。

因而,针对看板的状况,能够针对多个卡片做离屏烘托。多个卡片离屏烘托比整屏离屏烘托更有优势。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

看板翻滚首要有两种状况:

  • 第一种,没有呈现新的分组和卡片,当时仅仅在可视区域的卡片内翻滚;

  • 第二种,呈现了新的分组和卡片,触及到了节点的销毁和新增。

关于第一种状况来说,此刻没有新增卡片,多卡片离屏烘托只需求把离屏 Canvas 里边的内容制作到主屏就行了。但整屏离屏烘托仍然会去多烘托增量部分,由于它是以整个屏幕为纬度的;关于第二种状况来说,两者都需求制作增量部分的卡片,所以理论上耗费是一样的。

但在快速翻滚的状况下,大部分时刻都是没有呈现新的分组的,大概率是在可视区内的几个分组移动,所以这种状况下,假如运用整屏烘托,就不得不多去烘托一个分组。

7.2 完成

在创立 Group 的时分,增加一个 offscreen 选项,它会多创立一个离屏 Canvas。也便是 offscreenCanvas,这个 canvas 会依据主屏的 Group 里边的子元从来先制作一遍。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

在 Group 的实践制作办法 drawScene 办法里边,判别当时 Group 是否存在离屏 Canvas。假如存在离屏 Canvas,那就直接用 drawImage 的办法。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

那离屏的 Canvas 什么时分失效呢?由于看板的特别性,用户修正了某个单元格有或许形成宽高级信息的改变。所以不得不从头核算一遍,这个时分也会从头制作。

之前的节点都会被销毁掉,然后创立新的节点。因而这个时分从头创立了新的离屏 Canvas 就不会失效了。翻滚的时分同理,滚出屏幕外的节点被销毁了,新增的节点从头创立了离屏 Canvas。各位开发者能够看到最终的优化效果,制作的耗时只要 2 ms。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

但正如前面说的,离屏烘托仅仅针对现已烘托好的卡片进行的。那假如翻滚的时分,呈现了新的卡片怎样办?这部分烘托仍然会很耗时。

08

文本缓存

制作可复用的部分处理完了,但是制作增量的部分耗时仍然很高,常常能够到达 20 ms 。由于它需求先搜集 painter,然后去制作 widget。搜集部分耗时现已优化到很低了,但制作部分耗时仍然很高。那要怎样处理呢?

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

假如是在文本量不多的时分,这部分耗时现已十分低,每帧耗时降至 58 ms,但文本量大的时分耗时就增多了。从图上能够发现,耗时首要发生在文本的核算和制作上面。那文本核算了哪些呢?

  • 第一,假如给定文本宽度,那文本需求在哪个字符进行切断、换行;

  • 第二,文本最终一行的后面是否需求增加省略号。

文本换行和切断,在 Konva 里边进行了十分杂乱的核算。首要是对文本进行二分查找,顺次找到最终需求切断的字符方位。假如有换行符,需求对换行符进行特别处理。假如传入的切断办法是 ‘word’,那还需求对空格和-进行特别的处理。假如传入的是 ellipsis,那需求在最终一行增加省略号。

这些杂乱的核算本身会耗费一些时刻,其间通过二分查找也会很多调用 measureText 办法。那要怎样处理呢?看板由于需求记载用户前次翻开翻滚条的方位,再次翻开的时分需求跳转曩昔。为了防止翻滚的时分,再去实时核算当时应该新增或削减哪些卡片,会在最开端的时分一次性核算好一切的卡片宽高。

卡片宽度触及到文本、图片等宽高,也便是说最开端现已处理过文本核算,那这部分缓存起来不就好了?所以在最开端核算的时分能够把特点为 key、宽高级信息作为value 一同存入 cacheText 里边,然后在 setTextData 里边判别 cacheText 里边是否有缓存,假如有的话就不需求从头核算一遍了。

这里缓存了三个信息,分别是文本宽度、文本高度、文簿本串数组(被切断分成了好几个)。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

但这样还是会有一些问题:假如文本特别长的话,那 textArr 也会比较大,简单导致内存增长。咱们修正战略:不存 textArr,而是存每个子串完毕的 index 值(换行的 index 值)

别的,在最开端核算的时分,仅仅为了算出文本的高度,制作阶段最多只展现 4 行,超越 4 行就需求增加省略号,所以算出高度后还要判别是否超越了 4 行。假如直接用最开端核算的成果,它或许包含了超越 4 行的信息,导致制作阶段不精确。例如存了六行,那制作的时分需求制作前 4 行;但是省略号是在第六行,导致在第 4 行丢失了省略号。

因而需求依据事务进一步深度定制,针对 Text 进行一次封装。为了防止动到核算换行的逻辑,咱们增加了一个标志位,用于判别当时传入的 height 表示最大高度。

09

总结与考虑

腾讯文档团队优化后的FPS接近 60 帧,从 20 多帧提升到 58 帧左右,也便是提升了两倍多。

10w单元格滚动卡顿如何解决?腾讯文档的7个秘笈

在这期间,团队总结了相关经验:**应该尽量防止翻滚的时分有堵塞主线程的耗时操作。**许多地方不易被发现,如深复制、序列化、反序列化等等。一些杂乱又耗时的核算能够将核算作业的成果提早缓存起来,这样翻滚的时分就能够直接从缓存里边读取了。由于这里原本就需求在加载的时分去核算这些,所以就进行了一些改造,让其支撑缓存。

假如想不拖慢首屏烘托速度,还能够放到 Web Worker 里边去核算,比方多核算几个分组的文本信息。针对一些比较耗时的制作操作能够运用离屏烘托的形式来防止重复制作。这里还能够考虑运用原生的 Offscreen 合作 Web Worker 来发挥离屏烘托的优势。以上是本次共享全部内容,欢迎各位开发者在评论区共享交流。

你或许感兴趣的腾讯工程师著作

|微信全文查找耗时降94%?咱们用了这种方案

|腾讯工程师聊ChatGPT技能「文集」

|腾讯云开发者热门技能干货汇总

|一文读懂 Redis 架构演化之路

技能盲盒:前端|后端|AI与算法|运维|工程师文化

阅读原文