违规长时间运行的JavaScript任务需要xx ms

pro*_*mer 284 javascript dom google-chrome

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

[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms
Run Code Online (Sandbox Code Playgroud)

我正在开展一个小组项目,我不知道它来自哪里.这从未发生过.当参与该项目的其他人突然出现.如何找到导致此类警告的文件/功能?我一直在寻找答案,但主要是关于如何解决它的解决方案.如果我找不到问题的根源,我无法解决.

注意:在这种情况下,警告仅出现在chrome上.我试图使用Edge,没有得到任何类似的警告.我还没有在mozilla上测试它.

更新:我甚至得到错误jquery.min.js说:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
Run Code Online (Sandbox Code Playgroud)

vol*_*evo 248

更新:Chrome 58+默认隐藏了这些和其他调试消息.要显示它们,请单击"信息"旁边的箭头,然后选择"详细".

更新2:默认情况下,Chrome 57启用了"隐藏违规"功能.要重新打开它们,您需要启用过滤器并取消选中"隐藏违规"框.

当其他人参与该项目时突然出现

我认为你更有可能更新到Chrome 56.这个警告是一个(imo)精彩的新功能 - 如果你绝望,请关闭它,你的评估员将取消你的标记.其他浏览器存在潜在问题,但浏览器并没有告诉您存在问题.Chromium门票在这里但是没有真正有趣的讨论:https: //bugs.chromium.org/p/chromium/issues/detail?id = 662497

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

然而,它们值得调查和修复以提高应用程序的质量.这样做的方法是关注消息出现的情况,并进行性能测试以缩小问题发生的位置.开始性能测试的最简单方法是插入一些代码,如下所示:

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

    // Do the normal stuff for this function

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

如果您想获得更高级的功能,还可以使用Chrome的分析器:https: //developers.google.com/web/tools/chrome-devtools/rendering-tools/

或者使用像这样的基准测试库:https: //benchmarkjs.com/

一旦你发现一些需要很长时间的代码(50毫秒是Chrome的门槛),你有几个选择:

  1. 删除可能不必要的部分/全部任务
  2. 弄清楚如何更快地完成相同的任务
  3. 将代码分成多个异步步骤

(1)和(2)可能是困难的或不可能的.但有时真的很容易,应该是你的第一次尝试.如果需要,应始终可以做(3).要做到这一点,你将使用类似的东西

setTimeout(functionToRunVerySoonButNotNow);
Run Code Online (Sandbox Code Playgroud)

要么

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

你可以在这里阅读更多关于javascript的异步性质:

http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/

  • 很棒的答案,voltrevo!我的问题是,如果这样的代码是违规的,那究竟是什么违反了?Google正在应用某种标准,但该标准是否在任何地方公开记录? (28认同)
  • 只是一个建议,而不是使用`performance.now()`,你可以使用`console.time`(https://developer.mozilla.org/en-US/docs/Web/API/Console/time)`console .time('UniquetLabelName')....代码在这里.... console.timeEnd('UniqueLabelName')` (13认同)
  • @Bungler我只能猜测它是说动画的代码违反了每秒至少提供60帧,因此给用户带来了不好的体验.. (3认同)
  • @Bungler Dunno,我想知道它是否也提到了一些指南。 (2认同)

noo*_*oob 74

这些只是每个人都提到的警告.但是,如果您热衷于解决这些问题(您应该这样做),那么您需要首先确定导致警告的原因.没有任何理由可以获得强制回流警告.
有人为一些可能的选项创建了一个列表.您可以按照讨论了解更多信息.
以下是可能原因的要点 -

什么力量布局/回流

当在JavaScript中请求/调用时,以下所有属性或方法将触发浏览器同步计算样式和布局*.这也称为回流或布局颠簸,是常见的性能瓶颈.

元件

Box指标
  • elem.offsetLeft,elem.offsetTop,elem.offsetWidth,elem.offsetHeight,elem.offsetParent
  • elem.clientLeft,elem.clientTop,elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
滚动的东西
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft,elem.scrollTop同时,将它们设置
焦点
  • elem.focus() 可以触发双重强制布局()
也…
  • elem.computedRole, elem.computedName
  • elem.innerText(来源)

的getComputedStyle

window.getComputedStyle()通常会强制样式重新计算(来源)

window.getComputedStyle() 如果满足以下任何条件,则强制布局:

  1. 元素位于阴影树中
  2. 有媒体查询(与视口相关的查询).具体而言,执行以下操作之一:()*min-width,min-height,max-width,max-height,width,height *aspect-ratio,min-aspect-ratio,max-aspect-ratio
    • device-pixel-ratio,resolution,orientation
  3. 要求的财产是以下之一:( 来源)
    • height,width *top,right,bottom,left *margin[ -top,-right,-bottom,-left,或速记 ]仅当余量是固定的.*padding[ -top,-right,-bottom,-left,或速记 ]仅当填充是固定的.*transform, transform-origin,perspective-origin *translate,rotate, scale *webkit-filter,backdrop-filter *motion-path, motion-offset,motion-rotation *x,y,rx,ry

窗口

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() 只有力量风格

形式

  • inputElem.focus()
  • inputElem.select(),textareaElem.select()(来源)

鼠标事件

  • mouseEvt.layerX,mouseEvt.layerY,mouseEvt.offsetX,mouseEvt.offsetY ()

文献

  • doc.scrollingElement 只有力量风格

范围

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

SVG

CONTENTEDITABLE

  • 很多很多东西,包括将图像复制到剪贴板(来源)

在这里查看更多.

更新:

来自评论的 robocat

更多背景信息:原始问题中的Chromium源代码以及有关警告的性能API讨论.

更新2:
有一篇关于如何最大限度地减少Google的PageSpeed Insight上的布局重排的文章.它解释了什么是浏览器重排 -

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

以及如何最小化它 -

  1. 减少不必要的DOM深度.DOM树中一个级别的更改可能会导致树的每个级别发生更改 - 一直到根目录,并一直向下到已修改节点的子级.这导致更多时间用于执行回流.
  2. 最小化CSS规则,并删除未使用的CSS规则.
  3. 如果您进行复杂的渲染更改(如动画),请执行此操作.使用position-absolute或position-fixed来完成此任务.
  4. 避免使用不必要的复杂CSS选择器 - 特别是后代选择器 - 这需要更多的CPU功率来进行选择器匹配.

  • 综上所述,只要读取 element.scrollTop 就会触发回流。这在我看来是一种违反直觉的现象。我可以理解为什么 *setting* element.scrollTop 会触发回流,但只是读取它的值?如果确实如此,有人可以进一步解释为什么会这样吗? (2认同)

the*_*kim 30

一些想法:

  • 删除一半代码(可能通过评论).

    • 问题还存在吗?太棒了,你已经缩小了可能性!重复.

    • 问题不存在吗?好的,看看你注释掉的那一半!

  • 您使用的是任何版本控制系统(例如Git)吗?如果是这样,git checkout你最近的一些提交.问题何时出现?查看提交以确定在问题首次到达时确切地更改了哪些代码.

  • 您可以使用git bisect来应用二进制搜索。我认为这只是为了发现错误。 (2认同)

Mat*_*icz 8

就我而言,我发现这实际上是由扩展程序中的代码引起的(在我的案例中是Adblock).

要确定问题的根源,请运行您的应用程序,并将其记录在Chrome 中的" 性能"标签中.

在那里,您可以检查需要很长时间才能运行的各种功能.就我而言,在控制台中与警告相关联的那个是来自Adblock扩展加载的文件,但在您的情况下这可能是其他内容.

检查这些文件并尝试确定这是否是某些扩展代码或您的.


Jor*_*ick 6

在"网络"选项卡下的Chrome控制台中查找,并查找最长时间加载的脚本.

在我的情况下,我已经包含但尚未在应用程序中使用的脚本添加了一组Angular:

<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>
Run Code Online (Sandbox Code Playgroud)

这些是加载时间较长的JavaScript文件,而不是指定"长时间运行任务"错误的时间.

所有这些文件都在我的其他网站上运行,没有生成任何错误,但我在一个几乎没有任何功能的新网络应用程序上收到此"长时间运行任务"错误.删除后错误立即停止.

我最好的猜测是,这些Angular附加组件正在递归地看到DOM的越来越深的部分用于它们的开始标记 - 找不到,它们必须在退出之前遍历整个DOM,这比Chrome期望的要长 - 因此警告.


wf9*_*m75 5

我在 Apache Cordova 源代码中找到了解决方案。他们是这样实现的:

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

实施简单,但方式巧妙。

在 Android 4.4 以上,使用Promise. 对于较旧的浏览器,请使用setTimeout()


用法:

nextTick(function() {
  // your code
});
Run Code Online (Sandbox Code Playgroud)

插入此技巧代码后,所有警告消息都消失了。


Vit*_*ich 5

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

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');
});
Run Code Online (Sandbox Code Playgroud)

性能选项卡(分析器)显示事件耗时约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'));
});
Run Code Online (Sandbox Code Playgroud)

现在,“性能”选项卡(分析器)显示该事件大约需要1毫秒: 铬探伤剂暗

而且我认为搜索现在可以更快地进行(229个节点)。

  • 总之,通过收到违规,您可以优化代码,并且现在性能更好。 (2认同)

Jim*_*den 5

在此添加我的见解,因为该线程是有关该主题的“转到”stackoverflow 问题。

我的问题出在 Material-UI 应用程序中(早期阶段)

  • 自定义主题提供商的放置是原因

当我进行一些强制渲染页面的计算时(一个组件“显示结果”取决于其他组件“输入部分”中的设置)。

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

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

在 App.js 中:

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

        <SummaryAppBarPure
//...
Run Code Online (Sandbox Code Playgroud)

在index.js中

ReactDOM.render(
  <React.StrictMode>
      <App />
//...
Run Code Online (Sandbox Code Playgroud)

在 App.js 中:

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

        <SummaryAppBarPure
//...
Run Code Online (Sandbox Code Playgroud)

在index.js中

ReactDOM.render(
  <React.StrictMode>
    <MyThemeProvider>
      <App />
//...
Run Code Online (Sandbox Code Playgroud)