【问题标题】:Violation Long running JavaScript task took xx ms违规长时间运行的 JavaScript 任务花费了 xx 毫秒
【发布时间】:2021-05-06 07:45:49
【问题描述】:

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

[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

【问题讨论】:

  • 您在哪里看到此警告?你没有说你在什么环境中工作。假设是一些浏览器,但是是哪一个?
  • @SamiKuhmonen 抱歉,我已经更新了我的问题。我用的是铬。我在 Edge 上没有发现任何类似的错误。
  • 我只是想补充一点,这条 2016 年底推出的警告消息也可能由于您在 Chrome 中安装的任何扩展程序而出现。通过在私有模式下测试很容易检查。
  • 点击右侧链接,指出发生违规的脚本,将带您到代码中发生违规的地方。
  • 我使用的是 Ionic 4 (Angular 8),我的代码运行良好,突然这种违规行为开始出现 - 现在我的列表中没有数据显示?

标签: javascript google-chrome dom


【解决方案1】:

不管怎样,这是我遇到的 2 美分

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

警告。有问题的页面是从用户内容生成的,所以我对 DOM 的大小没有太大的影响。就我而言,问题是一个包含两列的表,可能有数百甚至数千行。 (还没有实现按需行加载,抱歉!)

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

有关此特定性能方案的更多详细信息,另请参阅this article

【讨论】:

    【解决方案2】:

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

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

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

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

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

    解决方案是将 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 />
    //...
    

    【讨论】:

      【解决方案3】:

      为了确定问题的根源,运行您的应用程序,并将其记录在 Chrome 的“性能”标签中

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

      检查这些文件并尝试确定这是某个扩展程序的代码还是您的代码。 (如果是你的,那么你已经找到了问题的根源。)

      【讨论】:

      • 不,我没有 AdBlock,我仍然可以在控制台中找到它。
      • 尝试使用性能选项卡对其进行分析,并寻找长时间运行的功能的来源。这可以是任何东西,但这是确定问题根源的一种潜在方法。
      【解决方案4】:

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

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

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

      我认为您更新到 Chrome 56 的可能性更大。在我看来,此警告是一个很棒的新功能,请仅在您绝望时将其关闭,并且您的评估员会从您身上扣分。其他浏览器中存在潜在问题,但浏览器并没有告诉您存在问题。 Chromium 票是here,但实际上并没有任何有趣的讨论。

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

      但是,它们值得研究和修复以提高您的应用程序的质量。这样做的方法是注意消息出现的情况,并进行性能测试以缩小问题发生的范围。开始性能测试的最简单方法是插入如下代码:

      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's profiler,或者使用像this one这样的基准测试库。

      一旦您发现一些需要很长时间的代码(50 毫秒是 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 here 的异步特性。

      【讨论】:

      • 只是一个建议,而不是使用performance.now(),您可以使用console.time (developer.mozilla.org/en-US/docs/Web/API/Console/time) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
      • @denislexic 我想是的。不过,我不确定真正增加了什么价值。我认为了解获取当前时间并以此为基础的基本操作更有价值。
      • 好答案,伏雷沃!我的问题是,如果这样的代码违反了,它到底违反了什么? Google 必须采用某种标准,但该标准是否公开记录在任何地方?
      • @Bungler Dunno,我想知道它是否也提到了一些指导方针。
      • @Bungler 我只能猜测这是在说动画代码违反了每秒至少 60 帧的规定,因此用户体验很差。 .
      【解决方案5】:

      这是来自 Google Chrome 的违规错误,当启用 Verbose 日志记录级别时显示。

      错误信息示例:

      说明:

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

      原始文章:Minimizing browser reflow,由 UX 开发人员 Lindsey Simon 发布,发布在 developers.google.com 上。

      this is the linkGoogle Chrome 会在性能分析器中的布局配置文件(淡紫色区域)中为您提供有关警告的更多信息。

      【讨论】:

        【解决方案6】:

        这不是错误,只是简单的消息。要执行此消息更改
        &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;(示例)

        &lt;!DOCTYPE html&gt;(Firefox 源期望这个)

        该消息显示在 Google Chrome 74 和 Opera 60 中。更改后很清楚,0 冗长。
        解决方法

        【讨论】:

        • 只是一些建议:您的回答与问题无关。修复您的答案或将其删除。问题是“为什么 Chrome 浏览器控制台显示违规警告”。答案是它是较新的 Chrome 浏览器中的一项功能,如果网页在执行 JS 时导致浏览器过度回流,它会提醒您。更多信息请参考this resource from Google
        【解决方案7】:

        当您在执行结束前多次调用函数时,通常会发生强制重排。

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

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

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

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

        【讨论】:

        • 这不是解决方案。建议最好将其作为对原始问题的评论。
        【解决方案8】:

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

        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 毫秒的事件:

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

        【讨论】:

        • 总之,通过收到违规,您可以优化您的代码,并且现在它的性能更好。
        【解决方案9】:

        这是在 Chrome 56 测试版中添加的,尽管它不在 Chromium 博客的此更改日志中:Chrome 56 Beta: “Not Secure” warning, Web Bluetooth, and CSS position: sticky

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

        【讨论】:

          【解决方案10】:

          如果您使用的是 Chrome Canary(或 Beta),只需选中“隐藏违规”选项即可。

          【讨论】:

            【解决方案11】:

            正如大家提到的,这些只是警告。但是,如果您热衷于解决这些问题(您应该这样做),那么您需要首先确定导致警告的原因。没有任何一个原因会导致您收到强制回流警告。 有人为一些可能的选项创建了list。您可以关注讨论以获取更多信息。
            以下是可能原因的要点:

            什么迫使布局/回流

            以下所有属性或方法,在请求/调用时 JavaScript,会触发浏览器同步计算 风格和布局*。这也称为回流或layout thrashing, 并且是常见的性能瓶颈。

            元素

            盒子指标
            • 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() 可以触发 double 强制布局 (source)
            还…
            • elem.computedRole, elem.computedName
            • elem.innerText (source)

            getComputedStyle

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

            window.getComputedStyle() 也会强制布局,如果有的话 以下是正确的:

            1. 元素位于阴影树中
            2. 有媒体查询(与视口相关的)。具体来说,以下之一: (source) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio, max-aspect-ratio
              • device-pixel-ratio, resolution, orientation
            3. 请求的属性是以下之一:(source)
              • height, width * top, right, bottom, left * margin [-top, -right, -bottom, @98764 /em>] 仅当 边距是固定的。 * padding [-top, -right, -bottom, -left, 或 shorthand] 仅当填充固定时。 * 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() (source)

            鼠标事件

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

            文档

            • doc.scrollingElement 只强制风格

            范围

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

            SVG

            内容可编辑

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

            查看更多here

            此外,这里是来自 original issue 的 Chromium 源代码和用于警告的 discussion about a performance API


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

            Reflow 是 Web 浏览器进程的名称,用于重新计算 文档中元素的位置和几何形状,用于 重新渲染部分或全部文档。因为回流是一种 浏览器中的用户阻止操作,这对开发人员很有用 了解如何改善回流时间并了解 各种文档属性的效果(DOM 深度、CSS 规则 效率,不同类型的风格变化)对回流时间。 有时重排文档中的单个元素可能需要 重排它的父元素以及跟随它的任何元素。

            另外,它解释了如何最小化它:

            1. 减少不必要的 DOM 深度。 DOM 树中一级的更改 可以导致树的每一层都发生变化 - 一直到 root,一直到修改节点的子节点。 这会导致花费更多时间来执行回流。
            2. 尽量减少 CSS 规则,并删除未使用的 CSS 规则。
            3. 如果您进行复杂的渲染更改(例如动画),请在流程之外进行。使用 position-absolute 或 position-fixed 来完成 这个。
            4. 避免不必要的复杂 CSS 选择器 - 中的后代选择器 特别是 - 这需要更多的 CPU 能力来进行选择器匹配。

            【讨论】:

            • 更多背景:来自original issue 的 Chromium 源代码和用于警告的discussion about a performance API
            • 根据上面的说法,只要读取 element.scrollTop 就会触发回流。这让我觉得这是一种反直觉的现象。我可以理解为什么 setting element.scrollTop 会触发重排,而只是读取它的值?如果确实是这样的话,有人可以进一步解释为什么会这样吗?
            【解决方案12】:

            我在 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
            });
            

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

            【讨论】:

              【解决方案13】:

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

              在我的例子中,我已经包含了一组 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 文件的加载时间比“长时间运行的任务”错误指定的时间长。

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

              我最好的猜测是,这些 Angular 插件会递归地查看 DOM 中越来越深的部分以获取它们的开始标签 - 没有找到,他们必须在退出之前遍历整个 DOM,这比 Chrome 预期的要长 - 因此发出警告.

              【讨论】:

                【解决方案14】:

                几个想法:

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

                  • 问题仍然存在吗?太好了,您已经缩小了可能性!重复。

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

                • 您是否使用任何版本控制系统(例如,Git)?如果是这样,git checkout 您最近的一些提交。问题是什么时候引入的?查看提交以确切了解问题首次出现时更改了哪些代码。

                【讨论】:

                • 感谢您的回答。我确实删除了一半,甚至从项目中排除了我的主要 .js 文件。不知何故,错误仍然发生。这就是为什么我对此感到如此沮丧。是的,我正在使用 git。我今天才意识到这个错误。自从这成为小组项目以来,已经有很多提交。可能会进行深入检查。再次感谢您的想法。
                • @procatmer 使用相同的策略来查找 git 提交。例如,如果我有 10 次提交(A、B、C、D、E、F、G、H、I、J),其中 A 是最旧的,我会git checkout E 来查看问题是否已经存在。如果是,我将继续在提交的前半部分寻找问题。不然我下半场找问题。
                • @procatmer 另外,如果您省略了主要的.js 文件并且问题仍然存在......它可能是您通过&lt;script src="..."&gt; 标签引入的库!也许有些事情不值得担心(尤其是因为它只是一个警告)?
                • 我终于找到了问题所在。我用你的第二个想法来跟踪变化。是的,问题来自外部.js 文件。显然,这很重要。它大大减慢了我的网站。无论如何,再次感谢您的回答和想法。
                • 你可以使用 git bisect 来应用二分搜索。我认为这只是为了发现错误。
                猜你喜欢
                • 2017-07-02
                • 1970-01-01
                • 2022-06-17
                • 2017-09-02
                • 2021-01-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多