避免 Web 中不必要的渲染
为站点或应用程序绘制元素可能会非常昂贵,并且会对我们的运行时性能产生负面的连锁反应。 在本文中,我们将快速了解什么可以触发浏览器中的绘画,以及如何防止发生不必要的绘画。
绘画:超级快速的游览
浏览器必须执行的主要任务之一是将您的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论