本网站内容使用人工智能(AI)或机器翻译技术翻译,可能存在错误。

Skip to content

通过轮播加载将前端内存使用率降低30倍

2020年第二季度,我们对“体验”页面进行了技术重构,将其重写为React并调用REST API。但在实施过程中,我们发现了一个有趣的问题。

“体验”页面上大约有 20 个轮播图,每个轮播图至少包含 60 个图块。因此,该页面将渲染超过 1,200 条体验内容,包括标题、用户评分、并发用户数和缩略图等相关信息。在每个轮播图中,点击右侧滚动条会发送请求以加载下一批体验内容(如果还有更多)。 随后,请求返回的新数据会被追加到轮播图中,这意味着会有更多节点被添加到 DOM 中,浏览器中渲染的缩略图也会随之增加。随着用户滚动页面以请求更多数据,浏览器内的内存占用会不断增长,进而导致用户体验变慢并开始出现卡顿。

内存剖析

我们可以使用 Chrome 开发者工具对该问题进行性能分析。第一个快照是在页面初始加载时捕获的,第二个快照是在用户在某个轮播图中点击“下一页”12 次后捕获的。我们发现,由于 100 多个字符串节点以及缩略图渲染量的增加,内存使用量增加了近 9.7MB。

解决方案

当轮播图滚动时,用户仅能看到轮播窗口内的项目。例如,在 1920×1080 分辨率的笔记本电脑上,全屏浏览器模式下每次仅会显示约 9 个可见项目。因此,没有必要同时渲染所有不可见的节点和缩略图,也不必将请求返回的更多数据加载到 DOM 中。

因此,我们的思路是:在首次从API请求获取原始数据后,构建一个长度与之相同的数组以备渲染到DOM中。在渲染数组内,我们只需填充足够的数据以填满当前显示区域并支持滚动到下一项。数组中的其余部分仅在显示窗口滚动至其附近时才会被填充。 让我们定义两个索引来记录渲染数据的范围:renderingStartIndex 和 renderingEndIndex。我们设置一个光标来标记列表中可见部分的起始位置。在滚动过程中,我们需要持续将光标更新为第一个可见项的索引。在滚动动画结束之前,我们需要检查下一个光标是否位于渲染数据数组范围内。 根据轮播窗口大小和卡片尺寸,我们可以轻松判断显示窗口内能容纳多少个可见项目。然后,我们需要确保当前窗口和下一个窗口中的项目都在渲染数据数组的范围内。如果不在,我们就从原始数据中获取项目来填充空缺列表,同时清除光标位置之前的数据。这适用于用户向右或向左滚动的情况。 当用户向右滚动时,通过在后台获取更多数据,我们会在每次收到新数据后用原始数据进行填充。不过,原始数据不会参与页面渲染。

改进后的效果如下:

内存概况

让我们再次查看 Chrome 开发者工具。与之前一样,第一个快照是在初始化时捕获的,第二个快照是在向右滚动 12 次后捕获的。现在,加载 100 多个项目仅增加了 0.3MB 的内存,而之前则增加了 9.7MB。

下一步是什么?

除了改进轮播组件以处理大型数据窗口外,在重构功能/页面时,我们需要添加更多基准测试来捕获性能信息。

尽管现代 Web 应用程序架构正逐渐向前端转移,但捕获和优化前端遥测数据与传统的后端遥测同样重要。

江颖是 Roblox 的首席前端软件工程师。作为 Roblox 的首位前端工程师,她致力于将现代前端技术栈引入 Roblox,并优化开发与部署流程。她还参与了聊天、通知流和语音等实时功能的开发。

Roblox 公司及本博客均不认可或支持任何公司或服务。此外,对于本博客所含信息的准确性、可靠性或完整性,不作任何保证或承诺。