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

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

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

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

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

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


当前回答

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

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

其他回答

更新: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异步特性的内容。

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

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个节点)。

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

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

在这里添加我的见解,因为这个帖子是关于这个主题的“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 />
//...