长时间运行JavaScript任务花费了xx毫秒

最近,我收到了这样的警告,这是我第一次收到:

[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms

我正在做一个小组项目,我不知道这是从哪里来的。这在以前从未发生过。突然,当其他人参与到这个项目中时,它出现了。如何查找导致此警告的文件/函数?我一直在寻找答案,但主要是关于如何解决它的解决方案。如果我连问题的根源都找不到,我就解决不了。

在这种情况下,警告只出现在Chrome上。我尝试使用Edge,但没有收到任何类似的警告,而且我还没有在Firefox上测试它。

我甚至从jquery.min.js中得到错误:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
620406 次浏览

这里有几个想法:

  • 删除一半的代码(可能通过注释)。

    • 问题还在吗?太好了,你缩小了可能性!重复。

    • 问题不在那里吗?好吧,看看你评论掉的那一半!

    • 李< / ul > < / >
    • 你是否使用任何版本控制系统(例如Git)?如果是,git checkout你最近的一些提交。这个问题是什么时候提出的?查看提交,看看问题第一次出现时到底修改了哪些代码。

这是在Chrome 56 beta中添加的,尽管它不在Chromium博客的更新日志中:Chrome 56 Beta:“不安全”警告,Web蓝牙和CSS position: sticky

你可以用隐藏侵犯复选框将其隐藏在控制台的过滤器栏中。

如果你使用Chrome金丝雀(或测试版),只需检查“隐藏违规”选项。

隐藏违规复选框在Chrome 56控制台

更新: Chrome 58+默认隐藏这些和其他调试消息。要显示它们,单击“信息”旁边的箭头,并选择“详细”。

Chrome 57默认开启“隐藏违规”。要打开它们,你需要启用过滤器并取消选中“隐藏违规”框。

突然,当其他人参与这个项目时,它就出现了

我觉得更可能是你更新到Chrome 56。这个警告是一个很好的新功能,在我看来,只有在你非常绝望,考官会扣你分的时候,才请关闭它。潜在的问题在其他浏览器中存在,但浏览器只是不告诉你有问题。铬票是在这里,但实际上没有任何有趣的讨论。

这些消息是警告而不是错误,因为它不会真正导致重大问题。它可能会导致帧被丢弃或导致不太流畅的体验。

但是,为了提高应用程序的质量,它们是值得研究和修正的。做到这一点的方法是关注消息出现的情况,并进行性能测试以缩小问题发生的位置。开始性能测试最简单的方法是插入如下代码:

function someMethodIThinkMightBeSlow() {
const startTime = performance.now();


// Do the normal stuff for this function


const duration = performance.now() - startTime;
console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}

如果你想要更高级,你也可以使用Chrome的分析器,或者使用像这一个这样的基准测试库。

一旦你发现一些代码需要很长时间(50ms是Chrome的阈值),你有几个选择:

  1. 砍掉一些/全部不必要的任务
  2. 弄清楚如何更快地完成同样的任务
  3. 将代码分成多个异步步骤

(1)和(2)可能很难或不可能,但有时真的很容易,应该是你的第一次尝试。如果需要,它应该总是可以做到(3)。要做到这一点,你将使用如下内容:

setTimeout(functionToRunVerySoonButNotNow);

// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);

你可以阅读更多关于JavaScript 在这里的异步特性。

在Chrome控制台中Network选项卡下查找加载时间最长的脚本。

在我的例子中,有一组Angular add on脚本,我已经包含了,但还没有在应用中使用:

<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>

只有这些JavaScript文件的加载时间比“长时间运行任务”错误指定的时间长。

所有这些文件都在我的其他网站上运行,没有产生错误,但我在一个几乎没有任何功能的新web应用程序上得到了这个“长时间运行任务”错误。删除后错误立即停止。

我最好的猜测是,这些Angular add on正在递归地寻找DOM中越来越深的部分来寻找它们的开始标签——没有找到,它们必须在退出之前遍历整个DOM,这比Chrome预期的时间要长——因此出现了警告。

正如大家提到的,这些只是警告。然而,如果您热衷于解决这些问题(这是应该的),那么您需要首先确定导致警告的原因。没有一个原因可以导致强制回流警告。 有人已经为一些可能的选项创建了列表
.
.
. 以下是可能原因的要点:

什么力量布局/回流

下面所有的属性或方法,当请求/调用时 JavaScript,会触发浏览器同步计算 样式和布局*。这也被称为reflow或布局 < / >, 并且是常见的性能瓶颈。< / p >

元素

盒子指标
  • __abc0, __abc1, __abc2, __abc3, __abc4
  • __abc0, __abc1, __abc2, __abc3
  • elem.getClientRects(), elem.getBoundingClientRect()
滚动的东西
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeftelem.scrollTop也是,设置它们
焦点
也……
  • elem.computedRole, elem.computedName
  • elem.innerText ()

getComputedStyle

window.getComputedStyle()通常强制样式recalc () < / p >

window.getComputedStyle()将强制布局,以及,如果任何

  1. 元素在阴影树中
  2. 有媒体查询(与视口相关的)。具体来说,是下列情况之一: () * min-width min-heightmax-width max-height, width, height * aspect-ratio min-aspect-ratio max-aspect-ratio
    • __abc0, __abc1, __abc2
    • 李< / ul > < / > 所请求的属性是以下之一:()
        <李> heightwidth * top right, bottom, left * margin [-top, -right, -bottomwidth0或width1]只有 保证金是固定的。* __abc0 [__abc1, __abc2, __abc3, __abc4, 或速记]仅当填充是固定的。* transform, __abc0, __abc1 * __abc2, __abc3, __abc0 * __abc1, __abc2 * __abc3, motion-offsetmotion-rotation * x yrx ry李< / > 李< / ul > < / >

窗口

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules()只强制样式

形式

  • inputElem.focus()
  • __abc0, __abc1 (__abc2)

鼠标事件

  • mouseEvt.layerXmouseEvt.layerYmouseEvt.offsetXmouseEvt.offsetY . 李() < / >

文档

  • doc.scrollingElement只强制样式

范围

  • range.getClientRects(), range.getBoundingClientRect()

SVG

contenteditable

查看更多在这里

另外,这里有来自原来的问题的Chromium源代码和用于警告的关于性能API的讨论


编辑:还有一篇关于如何最小化PageSpeed Insight by谷歌上的布局回流的文章。它解释了什么是浏览器回流:

Reflow是web浏览器重新计算的进程名 文档中元素的位置和几何形状 重新渲染部分或全部文档。因为回流流是a 在浏览器中进行用户阻塞操作,对开发人员很有用 了解如何提高回流时间,也要了解 各种文档属性的影响(DOM深度,CSS规则 效率,不同类型的风格变化)的回流时间。 有时,在文档中重新循环单个元素可能需要 回流它的父元素和它后面的任何元素

此外,它还解释了如何将其最小化:

  1. 减少不必要的DOM深度。DOM树中一个级别的更改 可以引起树的每一层的变化-一直到 根节点,一直到修改节点的子节点。
  2. 最小化CSS规则,删除不使用的CSS规则。
  3. 如果你做了复杂的渲染改变,比如动画,在流外做。使用绝对位置或固定位置来完成 李。< / >
  4. 避免不必要的复杂CSS选择器
  5. . specific -它需要更多的CPU能力来进行选择器匹配

为了确定问题的根源,运行你的应用程序,并记录在Chrome的性能选项卡

在那里,您可以检查运行时间较长的各种函数。在我的情况下,一个与警告相关的控制台是由AdBlock扩展加载的文件,但这可能是其他的东西在你的情况下。

检查这些文件,并尝试识别这是一些扩展的代码还是你的。(如果是你的问题,那么你已经找到了问题的根源。)

我在Apache Cordova源代码中找到了解决方案。 它们实现如下:

var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };

实现简单,但方法聪明。

在Android 4.4上,使用Promise。 对于旧的浏览器,使用setTimeout()


用法:

nextTick(function() {
// your code
});

插入这个恶作剧代码后,所有警告消息都消失了。

我在我的代码中找到了这条消息的根,它搜索并隐藏或显示节点(脱机)。这是我的代码:

search.addEventListener('keyup', function() {
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
node.classList.remove('hidden');
else
node.classList.add('hidden');
});
性能选项卡(分析器)显示事件花费了大约60毫秒: 铬性能分析器布局重新计算回流

现在:

search.addEventListener('keyup', function() {
const nodesToHide = [];
const nodesToShow = [];
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
nodesToShow.push(node);
else
nodesToHide.push(node);


nodesToHide.forEach(node => node.classList.add('hidden'));
nodesToShow.forEach(node => node.classList.remove('hidden'));
});
性能选项卡(分析器)现在显示事件花费大约1毫秒: Chromium profiler dark

而且我觉得现在搜索速度更快了(229个节点)。

强制回流流通常发生在一个函数在执行结束前被多次调用的情况下。

例如,您可能在智能手机上遇到这个问题,但在经典浏览器上没有。

我建议使用setTimeout来解决这个问题。

这不是很重要,但我重复一遍,当您多次调用一个函数时,而不是当函数花费超过50毫秒时,问题就会出现。我认为你的回答错了。

  1. 关闭1-by-1调用并重新加载代码,以查看它是否仍然产生错误。
  2. 如果第二个脚本导致错误,则根据违规的持续时间使用setTimeOut
这不是一个错误,只是一个简单的消息。执行此消息change
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(例子)< br > 到< br > <!DOCTYPE html>(Firefox源代码期望此)

该消息显示在谷歌Chrome 74和Opera 60。更改后,0 verbose.
解决方法

这是谷歌Chrome浏览器的违规错误,显示了何时启用Verbose日志级别。

错误信息示例:

截图of the warning

解释:

Reflow是web浏览器进程的名称,用于重新计算文档中元素的位置和几何形状,以重新呈现部分或全部文档。因为在浏览器中,回流流是一种用户阻塞操作,所以对于开发人员来说,了解如何提高回流流时间以及了解各种文档属性(DOM深度、CSS规则效率、不同类型的样式更改)对回流流时间的影响是很有用的。有时,在文档中重新循环单个元素可能需要重新循环它的父元素以及它后面的任何元素。

原文:最小化浏览器回流,作者:Lindsey Simon, UX开发人员,发表于developers.google.com。

这就是链接谷歌Chrome浏览器给你在性能分析器,在布局配置文件(淡紫色区域),对警告的更多信息。

在这里添加我的见解,因为这个帖子是关于这个主题的“go to”stackoverflow问题。

我的问题是在一个材质ui应用程序(早期阶段)

  • 放置自定义主题提供程序是原因

当我做了一些计算强制渲染页面 (其中一个组件“显示结果”取决于其他组件“输入部分”中设置的内容)。< / p >

一切都很好,直到我更新了强制“结果组件”重新呈现的“状态”。这里的主要问题是,我有一个材质ui主题(https://material-ui.com/customization/theming/#a-note-on-performance)在同一个渲染器(App.js / return..)作为“结果组件”,SummaryAppBarPure

解决方案是提升ThemeProvider一个级别(Index.js),并在这里包装App组件,从而不强迫ThemeProvider重新计算和绘制/布局/回流。

之前

在App.js:

  return (
<>
<MyThemeProvider>
<Container className={classes.appMaxWidth}>


<SummaryAppBarPure
//...

在index.js

ReactDOM.render(
<React.StrictMode>
<App />
//...

在App.js:

return (
<>
{/* move theme to index. made reflow problem go away */}
{/* <MyThemeProvider> */}
<Container className={classes.appMaxWidth}>


<SummaryAppBarPure
//...

在index.js

ReactDOM.render(
<React.StrictMode>
<MyThemeProvider>
<App />
//...

不管怎样,这是我在遇到

[Violation] Forced reflow while executing JavaScript took <N>ms

警告。所讨论的页面是由用户内容生成的,因此我对DOM的大小实际上没有多大影响。在我的例子中,问题是一个有两列的表,可能有数百甚至数千行。(没有按需行加载实现,对不起!)

使用jQuery,在keydown上,页面选择一组行并切换它们的可见性。我注意到在该集合上使用toggle()比使用hide()更容易触发警告;show()明确。

有关此特定性能场景的更多详细信息,请参见这篇文章

答案是,这是新Chrome浏览器的一个功能,如果网页在执行JS时导致过多的浏览器回流,它会提醒你。详情请参阅