避免 Web 中不必要的渲染

发布于 2022-05-03 19:46:56 字数 4467 浏览 960 评论 0

为站点或应用程序绘制元素可能会非常昂贵,并且会对我们的运行时性能产生负面的连锁反应。 在本文中,我们将快速了解什么可以触发浏览器中的绘画,以及如何防止发生不必要的绘画。

绘画:超级快速的游览

浏览器必须执行的主要任务之一是将您的 DOM 和 CSS 转换为屏幕上的像素,它通过一个相当复杂的过程来完成。 它首先读取标记,然后创建一个 DOM 树。 它对 CSS 做了类似的事情,并由此创建了 CSSOM。 然后将 DOM 和 CSSOM 结合起来,最终我们得到一个可以开始绘制一些像素的结构。

如果你想更深入地了解浏览器的工作原理,这里有 一篇关于 HTML5 Rocks 的深入文章 ,你应该看看!

绘画过程本身很有趣。 在 Chrome 中,DOM 和 CSS 的组合树被一些名为 Skia 。 如果你曾经玩过 canvas你会非常熟悉 element Skia 的 API; 有许多 moveTo- 和 lineTo风格的功能以及一堆更高级的功能。 基本上所有需要绘制的元素都被提炼成一组可以执行的 Skia 调用,输出是一堆位图。 这些位图被上传到 GPU,GPU 通过将它们合成在一起来帮助我们在屏幕上显示最终图片。

需要注意的是,Skia 的工作量直接受到您应用于元素的样式的影响。 如果您使用算法繁重的样式,那么 Skia 将有更多工作要做。 Colt McAnlis 写了 一篇关于 CSS 如何影响页面渲染权重 ,因此您应该阅读该文章以获得更多信息。

尽管如此,油漆工作需要时间来执行,如果我们不减少它,我们将超过大约 16 毫秒的帧预算。 用户会注意到我们错过了帧并将其视为卡顿,这最终会损害我们应用程序的用户体验。 我们真的不希望这样,所以让我们看看什么样的事情导致油漆工作是必要的,以及我们能做些什么。

滚动

每当您在浏览器中向上或向下滚动时,它都需要在内容出现在屏幕上之前重新绘制内容。 一切都很好,这只是一个小区域,但即使是这种情况,需要绘制的元素也可以应用复杂的样式。 所以仅仅因为你有一小块区域可以绘画并不意味着它会很快发生。

为了查看正在重新绘制的区域,您可以使用 Chrome 的 DevTools 中的“显示绘制矩形”功能(只需点击右下角的小齿轮)。 然后,在 DevTools 打开的情况下,只需与您的页面进行交互,您就会看到闪烁的矩形显示 Chrome 在何时何地绘制了页面的一部分。

在 Chrome DevTools 中显示绘制矩形

滚动性能对您网站的成功至关重要; 当您的网站或应用程序滚动不佳时,用户会真正注意到,并且他们不喜欢它。 因此,我们有既得利益在滚动期间保持油漆工作轻,这样用户就不会看到卡顿。

我之前写过 一篇关于滚动性能 ,如果你想了解更多关于滚动性能的细节,可以看看那篇文章。

互动

交互是绘制工作的另一个原因:悬停、点击、触摸、拖动。 每当用户执行其中一种交互时,比如说悬停,Chrome 将不得不重新绘制受影响的元素。 而且,与滚动一样,如果需要大而复杂的绘画,您将看到帧速率下降。

每个人都想要漂亮、流畅的交互动画,所以我们需要再次查看动画中改变的样式是否会花费我们太多时间。

不幸的组合

带有昂贵油漆的演示

如果我滚动并碰巧同时移动鼠标会发生什么? 完全有可能 无意中 与它“交互”,从而触发了昂贵的绘画。 反过来,这可能会推动我完成约 16.7 毫秒的帧预算(我们需要保持在该预算之下才能达到每秒 60 帧的时间)。 我 创建了一个演示 来向您展示我的意思。 希望当您滚动和移动鼠标时,您会看到悬停效果开始出现,但让我们看看 Chrome 的 DevTools 是如何做到的:

Chrome 的 DevTools 显示昂贵的帧

在上图中,您可以看到当我将鼠标悬停在其中一个块上时,DevTools 正在注册绘制工作。 为了说明这一点,我在演示中使用了一些疯狂的重型样式,因此我正在推动并偶尔通过我的框架预算。 我最不想做的就是不必要地进行这种绘画工作,尤其是在卷轴期间还有其他工作要做时!

那么我们如何才能阻止这种情况发生呢? 碰巧,修复很容易实现。 这里的诀窍是附加一个 scroll处理程序将禁用悬停效果并设置计时器以再次启用它们。 这意味着我们保证当您滚动时我们不需要执行任何昂贵的交互绘制。 当您停止足够长的时间后,我们认为重新打开它们是安全的。

通过更改此设置,您会影响应用程序的用户体验,因此请明智地进行交易。 在再次启用悬停效果之前,只有您和您的团队才能决定可接受的延迟水平。

这是代码:

// Used to track the enabling of hover effects
var enableTimer = 0;

/*
 * Listen for a scroll and use that to remove
 * the possibility of hover effects
 */
window.addEventListener('scroll', function() {
  clearTimeout(enableTimer);
  removeHoverClass();

  // enable after 1 second, choose your own value here!
  enableTimer = setTimeout(addHoverClass, 1000);
}, false);

/**
 * Removes the hover class from the body. Hover styles
 * are reliant on this class being present
 */
function removeHoverClass() {
  document.body.classList.remove('hover');
}

/**
 * Adds the hover class to the body. Hover styles
 * are reliant on this class being present
 */
function addHoverClass() {
  document.body.classList.add('hover');
}

如您所见,我们在 body 上使用了一个类来跟踪是否“允许”悬停效果,并且底层样式依赖于该类的存在:

/* Expect the hover class to be on the body
 before doing any hover effects */
.hover .block:hover {
 …
}

这就是它的全部!

结论

渲染性能对于享受您的应用程序的用户至关重要,您应该始终致力于将绘制工作量保持在 16 毫秒以下。 为了帮助您做到这一点,您应该 在整个开发过程 中集成使用 DevTools,以便在出现瓶颈时识别和修复它们。

无意的交互,特别是在重绘元素上,可能会非常昂贵并且会影响渲染性能。 如您所见,我们可以使用一小段代码来解决这个问题。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

兮颜

暂无简介

0 文章
0 评论
308 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文