违规 长时间运行的 JavaScript 任务耗时 xx 毫秒

IT技术 javascript google-chrome dom
2021-01-26 00:28:20

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

[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
6个回答

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

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

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

我认为你更新到 Chrome 56 的可能性更大。这个警告​​是一个很棒的新功能,在我看来,只有在你绝望的时候才关闭它,你的评估员会从你那里拿走分数。其他浏览器存在潜在问题,但浏览器并没有告诉您存在问题。Chromium 票在这里,但实际上没有任何有趣的讨论。

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

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

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

一旦你发现一些需要很长时间的代码(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 异步特性的更多信息

很好的答案,伏特雷沃!我的问题是,如果像这样的代码是违规的,那么它到底违反了什么?谷歌必须应用某种标准,但该标准是否在任何地方公开记录?
2021-03-14 00:28:20
只是一个建议,而不是使用performance.now(),您可以使用console.timedeveloper.mozilla.org/en-US/docs/Web/API/Console/timeconsole.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
2021-03-22 00:28:20
@Bungler 我只能猜测它是说正在制作动画的代码违反了每秒提供至少 60 帧的速度,因此用户体验很差。.
2021-03-31 00:28:20
@Bungler Dunno,我想知道是否也有一些指导方针。
2021-04-01 00:28:20
@denislexic 我想是的。不过,我不确定真正增加了什么value。我认为了解获取当前时间并以此为基础的底层操作更有value。
2021-04-03 00:28:20

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

什么迫使布局/回流

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

元素

框指标
  • 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.scrollLeftelem.scrollTop同样,设置它们
重点
  • elem.focus() 可以触发双重强制布局(来源
还…
  • elem.computedRole, elem.computedName
  • elem.innerText来源

获取计算样式

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

内容可编辑

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

在此处查看更多信息

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


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

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

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

  1. 减少不必要的 DOM 深度。DOM 树中某个级别的更改可能会导致树的每个级别发生更改 - 一直到根,一直到被修改节点的子节点。这导致执行回流所花费的时间更多。
  2. 最小化 CSS 规则,并删除未使用的 CSS 规则。
  3. 如果您进行复杂的渲染更改,例如动画,请在流程之外进行。使用 position-absolute 或 position-fixed 来实现这一点。
  4. 避免不必要的复杂 CSS 选择器——尤其是后代选择器——它们需要更多的 CPU 能力来进行选择器匹配。
根据上述,简单地读取 element.scrollTop 会触发回流。这在我看来是一种违反直觉的现象。我可以理解为什么设置element.scrollTop 会触发回流,但只是读取它的值?如果确实如此,有人可以进一步解释为什么会这样吗?
2021-04-03 00:28:20
更多背景:来自原始问题的 Chromium 源代码以及有关警告的性能 API讨论
2021-04-11 00:28:20

几个想法:

  • 删除一半的代码(也许通过注释掉它)。

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

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

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

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

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

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 毫秒的事件: Chromium 性能分析器布局重新计算回流

现在:

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 深色

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

总而言之,通过接收违规,您能够优化您的代码,现在它的性能更好。
2021-03-14 00:28:20

为了找出问题的根源,请运行您的应用程序,并将其记录在 Chrome 的 Performance 选项卡中

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

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

尝试使用性能选项卡对其进行分析,并寻找长时间运行的功能的来源。这可以是任何事情,但这是确定问题根源的潜在方法。
2021-03-13 00:28:20
不,我没有 AdBlock,我仍然在控制台中得到它。
2021-03-18 00:28:20