了解 Chrome Dev Tools 时间轴

3

我正在尝试理解为什么Chrome开发者工具报告了多个“长帧”。

enter image description here

火焰图中的第一行(调用栈顶部)主要是由计时器触发事件组成,这些事件是由执行一堆$(function(){ });准备函数的jQuery.Deferred()引起的。

如果我深入研究jQuery源代码,并将它们对setTimeout的使用替换为requestAnimationFrame,那么火焰图并没有太大变化,我仍然会得到许多rAF在单个帧内触发(如开发工具所报告),导致长时间的帧。我本来期望做以下伪代码:

window.requestAnimationFrame(function() {
    // do stuff
});

window.requestAnimationFrame(function() {
    // do more stuff
});

是否在两个不同的动画帧上执行?这不是这样吗?

正在执行的所有JS都是必要的,但我该如何将其作为“微任务”执行(如此处所示提示但未加说明https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution),当setTimeoutrAF似乎无法实现时应该怎么办。

更新

这是一个放大的截图,显示了一个没有任何重排(强制或其他)的长帧。为什么所有rAF回调都在一个帧中执行?

enter image description here


显然,第一个rAF超出了帧预算,因此浏览器切换到长帧并执行了第二个rAF。也许只需使用一个rAF:多个requestAnimationFrame性能至于microtasks,您可以通过使用MutationObserver在某些不可见节点上启用例如attributes:true,然后更改属性来触发其中之一。 - wOxxOm
@wOxxOm 谢谢,理论上讲很有道理,但我不明白它是如何实现的,特别是在我添加的第二个截图中。 - Andrew Bullock
1个回答

6
长帧通常是由于强制同步布局引起的,这是指您(无意中)强制提前执行布局操作。
当您写入DOM时,布局需要重新流动,因为它已被写入操作使其无效。这通常会在下一帧发生。但是,如果您尝试从DOM中读取,布局会在当前帧中提前发生,以确保返回正确的值。当出现强制布局时,会导致长帧,从而导致卡顿。
为了防止这种情况发生,您应该仅在requestAnimationFrame函数内执行写操作。读操作应在此之外进行,以避免浏览器进行早期布局。 诊断强制同步布局是一篇解释得很好的文章,其中包含一个简单的示例演示如何在DevTools中检测强制回流以及如何解决它。

值得一提的是 FastDom,它是一个用于批量读写的库。它基本上是一个队列系统,更具可扩展性。

额外资料:什么会强制布局/回流,作者为 Paul Irish,其中包含了一份全面的属性和方法列表,这些属性和方法会强制进行布局/回流。

更新:关于多个requestAnimationFrame调用将在单独的帧上执行回调的假设,事实并非如此。当您有连续的调用时,浏览器会将回调添加到文档动画回调列表中。当浏览器去运行下一帧时,它遍历文档列表并按添加顺序依次执行每个回调函数。

请参阅 HTML 规范中的 Animation Frames 以获取更多实现详细信息。

这意味着你应该避免使用连续的调用,尤其是在回调函数执行时间总和超过你的帧预算的地方。我认为这可以解释那些不是由重排引起的长帧。


嗨,@Gideon,谢谢,我已更新了截图以显示更多高度。 右侧的3个强制回流是故意的(由于页面设计)。其他没有强制回流的帧呢?它们也很“长”,但Chrome并没有抱怨强制回流?你知道为什么多个rAF回调被批处理到一个单独的帧中,使其变长吗? - Andrew Bullock
我稍微看了一下规范,发现每次调用requestAnimationFrame函数时,浏览器都会将动画回调附加到文档级别的回调列表中,然后在浏览器运行它们的时间到来时遍历并调用它们。因此,您实际上是将您的回调合并成一个帧,使其变长。 - Gideon Pyzer
是的,我只是用 fastdom.defer 替换了所有我的 jquery.ready setTimeouts,现在它们都在自己的帧中执行。WinRAR - Andrew Bullock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接