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

JavaScript任务长时间运行,耗时234ms 执行JavaScript时强制回流花了45毫秒

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

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

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

[违规]Handler占用了231ms的运行时(允许50ms


这里有几个想法:

删除一半的代码(可能通过注释)。 问题还在吗?太好了,你缩小了可能性!重复。 问题不在那里吗?好吧,看看你评论掉的那一半! 你是否使用任何版本控制系统(例如Git)?如果是,git签出一些最近的提交。这个问题是什么时候提出的?查看提交,看看问题第一次出现时到底修改了哪些代码。


这是在Chrome 56测试版中添加的,尽管它不在Chrome博客的更新日志中:Chrome 56测试版:“不安全”警告,Web蓝牙和CSS位置:粘滞

您可以使用“隐藏违规”复选框将其隐藏在控制台的过滤器栏中。


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


更新: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)。要做到这一点,你将使用如下内容:

setTimeout(functionToRunVerySoonButNotNow);

or

// 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预期的时间要长——因此出现了警告。


正如大家提到的,这些只是警告。然而,如果您热衷于解决这些问题(这是应该的),那么您需要首先确定导致警告的原因。没有一个原因可以导致强制回流警告。 有人列出了一些可能的选择。您可以跟随讨论了解更多信息。 以下是可能原因的要点:

What forces layout / reflow All of the below properties or methods, when requested/called in JavaScript, will trigger the browser to synchronously calculate the style and layout*. This is also called reflow or layout thrashing, and is common performance bottleneck. Element Box metrics elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight elem.getClientRects(), elem.getBoundingClientRect() Scroll stuff elem.scrollBy(), elem.scrollTo() elem.scrollIntoView(), elem.scrollIntoViewIfNeeded() elem.scrollWidth, elem.scrollHeight elem.scrollLeft, elem.scrollTop also, setting them Focus elem.focus() can trigger a double forced layout (source) Also… elem.computedRole, elem.computedName elem.innerText (source) getComputedStyle window.getComputedStyle() will typically force style recalc (source) window.getComputedStyle() will force layout, as well, if any of the following is true: The element is in a shadow tree There are media queries (viewport-related ones). Specifically, one of the following: (source) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio, max-aspect-ratio device-pixel-ratio, resolution, orientation The property requested is one of the following: (source) height, width * top, right, bottom, left * margin [-top, -right, -bottom, -left, or shorthand] only if the margin is fixed. * padding [-top, -right, -bottom, -left, or shorthand] only if the padding is fixed. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx, ry window window.scrollX, window.scrollY window.innerHeight, window.innerWidth window.getMatchedCSSRules() only forces style Forms inputElem.focus() inputElem.select(), textareaElem.select() (source) Mouse events mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY (source) document doc.scrollingElement only forces style Range range.getClientRects(), range.getBoundingClientRect() SVG Quite a lot; haven't made an exhaustive list , but Tony Gentilcore's 2011 Layout Triggering List pointed to a few. contenteditable Lots & lots of stuff, …including copying an image to clipboard (source)

点击这里查看更多信息。

另外,这里是原始问题的Chromium源代码,以及关于警告的性能API的讨论。


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

Reflow is the name of the web browser process for re-calculating the positions and geometries of elements in the document, for the purpose of re-rendering part or all of the document. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow time and also to understand the effects of various document properties (DOM depth, CSS rule efficiency, different types of style changes) on reflow time. Sometimes reflowing a single element in the document may require reflowing its parent elements and also any elements which follow it.

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

Reduce unnecessary DOM depth. Changes at one level in the DOM tree can cause changes at every level of the tree - all the way up to the root, and all the way down into the children of the modified node. This leads to more time being spent performing reflow. Minimize CSS rules, and remove unused CSS rules. If you make complex rendering changes such as animations, do so out of the flow. Use position-absolute or position-fixed to accomplish this. Avoid unnecessary complex CSS selectors - descendant selectors in particular - which require more CPU power to do selector matching.


为了确定问题的根源,运行你的应用程序,并记录在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毫秒:

Now:

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毫秒:

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


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

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

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

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

关闭1-by-1调用并重新加载代码,以查看它是否仍然产生错误。 如果第二个脚本导致错误,则根据违规的持续时间使用setTimeOut。


这不是一个错误,只是一个简单的消息。执行此消息更改 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(示例) 来 <!DOCTYPE html>(Firefox源代码期望此) 该消息显示在谷歌Chrome 74和Opera 60。改变后它是清晰的,0 verbose。 解决方法


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

错误信息示例:

解释:

Reflow is the name of the web browser process for re-calculating the positions and geometries of elements in the document, for the purpose of re-rendering part or all of the document. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow time and also to understand the effects of various document properties (DOM depth, CSS rule efficiency, different types of style changes) on reflow time. Sometimes reflowing a single element in the document may require reflowing its parent elements and also any elements which follow it.

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

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


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

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

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

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

一切都很好,直到我更新了强制“结果组件”重新呈现的“状态”。这里的主要问题是,我有一个材质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,在按下键时,页面选择一组行并切换它们的可见性。我注意到,在该集合上使用toggle()比显式使用hide() & show()更容易触发警告。

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


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