違反長時間実行されるJavaScriptタスクにxxミリ秒かかりました


330

最近、私はこの種の警告を受けました、そしてそれは私がそれを初めて受け取るのです:

[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

この警告はどこに表示されますか?どの環境で作業しているかは言いません。ブラウザを想定していますが、どのブラウザを想定していますか?
Sami Kuhmonen 16

3
@SamiKuhmonen申し訳ありません。質問を更新しました。Chromeを使用しました。Edgeで同様のエラーは見つかりませんでした。
procatmer '19

8
2016年後半に導入されたこの警告メッセージは、Chromeにインストールした拡張機能が原因で表示されることもあります。プライベートモードでテストすることで、簡単に確認できます。
2016

1
違反が発生したスクリプトを示す右側のリンクをクリックすると、コード内の違反が発生した場所に移動します。
bluehipy 2017

Ionic 4(Angular 8)を使用していますが、コードは正常に機能していましたが、突然この種の違反が発生し始めました-現在、リストに表示されているデータはありませんか?
Kapil Raghuwanshi

回答:


278

更新: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のプロファイラーを使用するか、このようなベンチマークライブラリを利用することもできます。

長時間かかるコード(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の非同期性について詳しくは、こちらをご覧ください


16
単なる提案performance.now()ですが、を使用する代わりにconsole.timedeveloper.mozilla.org/en-US/docs/Web/API/Console/time)を 使用できますconsole.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic

@denislexic私はそう思う。しかし、それが実際にどのような価値をもたらすかはわかりません。現在の時刻を取得し、それに基づいて構築する基本的な操作について学ぶことは、より価値があると私は主張します。
voltrevo 2017年

34
素晴らしい答え、ボルトレボ!私の質問は、このようなコードが違反である場合、正確には何に違反していますか?Googleが適用しているある種の標準が必要ですが、その標準はどこかに公に文書化されていますか?
バングラー2017年

1
@Bungler Dunno、それが言及しているガイドラインがあるかどうか知りたいのですが。
voltrevo 2017年

4
@Bunglerアニメーションしているコードは少なくとも毎秒60フレームを提供することに違反しているため、ユーザーエクスペリエンスが低下しているとしか言えません。。
user895400

90

みんなが言ったように、これらは単なる警告です。ただし、これらを解決することに熱心である場合は(解決する必要があります)、最初に警告の原因を特定する必要があります。強制リフロー警告が表示される理由は1つではありません。誰かがいくつかの可能なオプションのリストを作成しました。詳細については、ディスカッションをフォローしてください。
考えられる理由の要点は次のとおりです。

レイアウト/リフローを強制するもの

以下のすべてのプロパティまたはメソッドは、JavaScriptでリクエストまたは呼び出されると、ブラウザーでスタイルとレイアウトを同期的に計算します*。これはリフローまたはレイアウトスラッシングとも呼ばれ、一般的なパフォーマンスのボトルネックです。

素子

ボックス指標
  • elem.offsetLeftelem.offsetTopelem.offsetWidthelem.offsetHeightelem.offsetParent
  • elem.clientLeftelem.clientTopelem.clientWidthelem.clientHeight
  • elem.getClientRects()elem.getBoundingClientRect()
スクロールのもの
  • elem.scrollBy()elem.scrollTo()
  • elem.scrollIntoView()elem.scrollIntoViewIfNeeded()
  • elem.scrollWidthelem.scrollHeight
  • elem.scrollLeftelem.scrollTopまた、それらを設定する
フォーカス
  • elem.focus()二重強制レイアウトを トリガーできます(ソース
また…
  • elem.computedRoleelem.computedName
  • elem.innerTextソース

getComputedStyle

window.getComputedStyle()通常、スタイルの再計算を強制します(ソース

window.getComputedStyle() 次のいずれかに該当する場合は、レイアウトも強制されます。

  1. 要素はシャドウツリーにあります
  2. メディアクエリ(ビューポート関連のクエリ)があります。具体的には、次のいずれか:(ソース)* min-widthmin-heightmax-widthmax-heightwidthheight * aspect-ratiomin-aspect-ratiomax-aspect-ratio
    • device-pixel-ratioresolutionorientation
  3. リクエストされたプロパティは次のいずれかです:(source
    • heightwidth * toprightbottomleft * margin[ -top-right-bottom-left、または速記 ]マージンが固定されている場合のみ。* padding-top-right-bottom-left、または速記 ]パディングが固定されている場合のみ。* transformtransform-originperspective-origin * translaterotatescale * webkit-filterbackdrop-filter * motion-pathmotion-offsetmotion-rotation * xyrxry

  • window.scrollXwindow.scrollY
  • window.innerHeightwindow.innerWidth
  • window.getMatchedCSSRules() スタイルのみを強制する

フォーム

  • inputElem.focus()
  • inputElem.select()textareaElem.select()ソース

マウスイベント

  • mouseEvt.layerXmouseEvt.layerYmouseEvt.offsetXmouseEvt.offsetYソース

資料

  • doc.scrollingElement スタイルのみを強制する

範囲

  • range.getClientRects()range.getBoundingClientRect()

SVG

コンテンツ編集可能

  • たくさんのもの、…画像をクリップボードにコピーすることを含む(ソース

詳しくはこちらをご覧ください

また、元の問題からのChromiumソースコードと警告のパフォーマンスAPIについての説明もここにあります。


編集:PageSpeed Insight by Googleでレイアウトのリフローを最小限に抑える方法に関する記事もあります。ブラウザのリフローについて説明しています。

リフローは、ドキュメントの一部またはすべてを再レンダリングする目的で、ドキュメント内の要素の位置とジオメトリを再計算するためのWebブラウザプロセスの名前です。リフローはブラウザーでのユーザーブロッキング操作であるため、リフロー時間を改善する方法を理解し、リフローに対するさまざまなドキュメントプロパティ(DOMの深さ、CSSルールの効率、さまざまなタイプのスタイル変更)の影響を理解することは、開発者にとって役立ちます。時間。場合によっては、ドキュメント内の単一の要素をリフローするときに、その親要素とそれに続く要素もリフローする必要があります。

さらに、それを最小化する方法についても説明します。

  1. 不要なDOMの深さを減らします。DOMツリーの1つのレベルでの変更は、ツリーのすべてのレベルで変更を引き起こす可能性があります-ルートまで、そして変更されたノードの子までずっと。これにより、リフローの実行により多くの時間が費やされます。
  2. CSSルールを最小化し、未使用のCSSルールを削除します。
  3. アニメーションなどの複雑なレンダリングの変更を行う場合は、フローの外で行います。これを行うには、位置絶対または位置固定を使用します。
  4. セレクタのマッチングを行うためにより多くのCPU能力を必要とする、不要な複雑なCSSセレクタ、特に子孫のセレクタは避けてください。

1
その他の背景:元の問題のChromiumソースコードと警告のパフォーマンスAPIに関する説明
robocat 2018年

1
上記によると、単にelement.scrollTopを読み取るとリフローがトリガーされます。これは、直感に反する現象として私を襲います。element.scrollTopを設定するとリフローがトリガーされる理由を理解できますが、単にその値を読み取るだけですか?これが事実である場合、誰かがさらに理由を説明できますか?
デビッドエドワーズ

29

いくつかのアイデア:

  • コードの半分を削除します(おそらくコメント化することにより)。

    • 問題はまだありますか?可能性を絞り込みました。繰り返す。

    • 問題はありませんか?コメントアウトした半分を見てください!

  • バージョン管理システム(Gitなど)を使用していますか?もしそうならgit checkout、あなたの最近のコミットのいくつか。問題はいつ導入されましたか?コミットを見て、問題が最初に発生したときに変更されたコードを正確に確認してください。


お返事ありがとうございます。プロジェクトから半分を削除し、メインの.jsファイルを除外しました。どういうわけかエラーはまだ発生しました。これが私がとてもイライラしている理由です。ええ、gitを使用しています。今日このエラーに気づきました。これがグループプロジェクトになって以来、多くのコミットがありました。詳細なチェックを行う場合があります。アイデアをありがとう。
procatmer '19

@procatmerは、gitコミットの検索と同じ戦略を使用します。たとえば、Aが最も古い10のコミット(A、B、C、D、E、F、G、H、I、J)があるgit checkout E場合、問題がすでに存在するかどうかを確認します。はいの場合は、コミットの前半で引き続き問題を探します。それ以外の場合は、後半で問題を探します。
therobinkim 16

1
@procatmerまた、メイン.jsファイルを省略しても問題が解決しない場合は、<script src="...">タグを介して読み込んだライブラリである可能性があります。おそらく心配する価値のないものかもしれません(特にそれは単なる警告なので)?
therobinkim、16

1
問題がどこにあるのか、ようやく見つかりました。変更を追跡するために2番目のアイデアを使用しました。はい、問題は外部.jsファイルに起因します。どうやら、それは重要です。それは私のサイトをかなり遅くします。とにかく、あなたの答えとアイデアをありがとう。
procatmer '19

2
git bisectを使用してバイナリ検索を適用できます。バグを発見するためだけのものだと思います。
pietrovismara 2017年

12

問題の原因を特定するには、アプリケーションを実行し、それをChromeの[パフォーマンス]タブに記録します

実行に時間がかかったさまざまな機能を確認できます。私の場合、コンソールの警告と相関したものは、AdBlock拡張機能によってロードされたファイルからのものでしたが、これはあなたの場合には他の何かである可能性があります。

これらのファイルを確認し、これが拡張機能のコードか自分のコードかを確認してください。(それがあなたのものであれば、あなたはあなたの問題の原因を見つけました。)


いいえ、AdBlockがなくてもコンソールに表示されます。
NikolaStojaković2017年

[パフォーマンス]タブで分析して、長時間実行される関数のソースを探します。これは何でもかまいませんが、これは問題の原因を特定する潜在的な方法です。
Matt Leonowicz 2017年

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>

これらは、「長時間実行タスク」エラーが指定した時間よりもロードに時間がかかった唯一のJavaScriptファイルでした。

これらのファイルはすべて他のWebサイトで実行され、エラーは生成されませんでしたが、ほとんど機能のない新しいWebアプリでこの「長時間実行タスク」エラーが発生しました。削除するとすぐにエラーが停止した。

私の推測では、これらのAngularアドオンは、開始タグのDOMのますます深いセクションを再帰的に調べていました-何も見つからなかったため、終了する前にDOM全体を走査する必要があり、Chromeの予想よりも時間がかかったため、警告が表示されました。


6

このメッセージのルートをコードで見つけました。コードを検索し、ノードを非表示または表示しました(オフライン)。これは私のコードでした:

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ミリ秒かかるイベントが表示されます。 クロムプロファイラーダーク

そして、検索がより速く機能するように感じます(229ノード)。


3
要約すると、違反を受け取ることにより、コードを最適化することができ、パフォーマンスが向上します。
ユーザーではないユーザー

3

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

このトリックコードを挿入すると、すべての警告メッセージが消えます。



2

Chrome Canary(またはBeta)を使用している場合は、[違反を非表示]オプションをオンにします。

Chrome 56コンソールで違反チェックボックスを非表示にする


1

これは、Verboseログレベルが有効になっているときに表示されるGoogle Chromeの違反エラーです。

エラーメッセージの例:

警告のスクリーンショット

説明:

リフローは、ドキュメントの一部またはすべてを再レンダリングする目的で、ドキュメント内の要素の位置とジオメトリを再計算するためのWebブラウザプロセスの名前です。リフローはブラウザーでのユーザーブロッキング操作であるため、リフロー時間を改善する方法を理解し、リフローに対するさまざまなドキュメントプロパティ(DOMの深さ、CSSルールの効率、さまざまなタイプのスタイル変更)の影響を理解することは、開発者にとって役立ちます。時間。場合によっては、ドキュメント内の単一の要素をリフローするときに、その親要素とそれに続く要素もリフローする必要があります。

元の記事:ブラウザーのリフローを最小限に抑える UX開発者であるLindsey Simonによる、developers.google.comに投稿されています。

そしてこれは、リンクされた Google Chromeは警告に関する詳しい情報は、レイアウトプロファイル(藤色の地域)で、パフォーマンスプロファイラであなたに与えます。


0

このスレッドは、トピックに関する「行き先」のスタックオーバーフローの質問だったので、ここに私の洞察を追加します。

私の問題はMaterial-UIアプリにありました(初期段階)

  • カスタムテーマプロバイダーの配置が原因でした

ページのレンダリングを強制するいくつかの計算を行ったとき(1つのコンポーネント、「結果の表示」は、他の「入力セクション」の設定に依存します)。

「結果コンポーネント」を強制的に再レン​​ダリングする「状態」を更新するまで、すべてが順調でした。ここでの主な問題は、同じレンダラー(App.js / return ..)にmaterial-uiテーマ(https://material-ui.com/customization/theming/#a-note-on-performance)があったことです。 「結果コンポーネント」として、SummaryAppBarPure

解決策は、ThemeProviderを1レベル上げ(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 />
//...

-2

実行が終了する前に関数が複数回呼び出されると、強制リフローが頻繁に発生します。

たとえば、スマートフォンでは問題が発生しても、従来のブラウザでは問題が発生しない場合があります。

私は setTimeout問題を解決するにはをます。

これはそれほど重要ではありませんが、繰り返しますが、問題は、関数を数回呼び出すときに発生し、関数が50ミリ秒以上かかる場合には発生しません。あなたの答えは間違っていると思います。

  1. 1行1列の呼び出しをオフにし、コードを再読み込みして、それでもエラーが発生するかどうかを確認します。
  2. 2番目のスクリプトがエラーの原因setTimeOutである場合は、違反の期間に基づいてを使用します。

これは解決策ではありません。元の質問へのコメントとして残したほうがよい提案です。
Paul-Sebastian Manole

-6

これは単なるメッセージではなく、エラーではありません。このメッセージを実行するには
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(例)

<!DOCTYPE html>(Firefoxソースはこれを想定)に変更します

。メッセージはGoogle Chrome 74およびOpera 60で表示されました。変更後は明らかで、詳細は0でした。
ソリューションアプローチ


5
ちょうどいくつかのアドバイス:あなたの答えは質問とは何の関係もありません。回答を修正するか、削除してください。問題は、「Chromeブラウザコンソールに違反警告が表示される理由」でした。答えは、それが新しいChromeブラウザーの機能であり、JSの実行中にWebページがブラウザーの過度のリフローを引き起こす場合に警告する機能です。詳細については、Googleのこのリソースを参照しください。
Paul-Sebastian Manole
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.