我试图了解为什么Chrome Dev Tools报告了多个长帧。
火焰图表中的第一行(调用堆栈的顶部)主要是Timer Fired
个事件,由jQuery.Deferred()
执行一堆$(function(){ });
就绪函数触发。
如果我深入研究jQuery源代码并将setTimeout
替换为requestAnimationFrame
,火焰图表的变化不大,我仍然会在一个帧内触发许多rAF(如dev所报告的那样)制作长帧的工具。我曾期望做下面的伪代码:
window.requestAnimationFrame(function() {
// do stuff
});
window.requestAnimationFrame(function() {
// do more stuff
});
在两个不同的动画帧上执行。情况不是这样吗?
正在执行的所有JS都是必要的,但我应该怎样执行它作为"微任务" (正如setTimeout
和rAF
似乎无法实现此目的时所暗示的https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution)。
更新
下面是其中一个看似没有任何回流(强迫或其他)的长帧的放大镜头。为什么这里的所有rAF回调都在一帧中执行?
由于
答案 0 :(得分:4)
长帧通常由强制同步布局引起,这是您(无意中)强制布局操作提前发生的时间。
当您写入DOM时,需要重新布局布局,因为它已被写操作无效。这通常发生在下一帧。但是,如果您尝试从DOM读取,则布局会在当前帧中提前发生,以确保返回正确的值。当强制布局发生时,它会导致长帧,导致抖动。
为防止这种情况发生,您应该只在requestAnimationFrame
功能中执行写操作。读取操作应该在此之外完成,以避免浏览器进行早期布局。
Diagnose Forced Synchronous Layouts是一篇很好解释的文章,有一个简单的示例演示,用于检测DevTools中的强制重排,以及如何解决它。
也许值得查看FastDom,这是一个用于批量读写的库。它基本上是一个排队系统,并且更具可扩展性。
其他来源: Paul Irish提供的What forces layout / reflow包含一系列强制布局/重排的属性和方法。
更新:对于多个requestAnimationFrame
调用将在不同的帧上执行回调的假设,情况并非如此。当您有连续调用时,浏览器会将回调添加到动画回调的文档列表中。当浏览器运行下一帧时,它会遍历文档列表并按照添加的顺序执行每个回调。
有关更多实施详细信息,请参阅HTML规范中的Animation Frames。
这意味着您应该避免使用连续调用,特别是在回调函数执行时间合并超出帧预算的情况下。我认为这可以解释不是由回流引起的长帧。