フロントエンドのパフォーマンス最適化-再描画とリフローを削減

ページが読み込まれると、ブラウザは取得したHTMLコードをDOMツリーに解析します。DOMツリーには、display:none非表示、JSで動的に追加された要素など、すべてのHTMLタグが含まれています。
  ブラウザはすべてのスタイル(ユーザー定義のCSSおよびユーザーエージェント)を解析して、スタイル構造の
  DOMツリーとスタイル構造の組み合わせを作成し、レンダーツリーを作成します。レンダーツリーはDOMツリーと似ていますが、レンダーツリーがスタイルを認識できるため、大きな違いがあります。 Render Treeの各ノードには独自のスタイルがあり、Render Treeには非表示のノード(display:noneノード、headノードなど)が含まれていません。これらのノードはレンダリングに使用されず、レンダリングに影響を与えないためです。そのため、Render Treeには含まれません。私自身の簡単な理解は、DOMツリーと一緒に作成したCSSの後で、レンダーツリーがレンダリングされるということです。

逆流

要素のサイズ、レイアウト、および非表示の変更により、Render Treeの一部またはすべてを再構築する必要がある場合、リフローと呼ばれます。ページレイアウトと幾何学的プロパティが変更されると、リフローが必要になります。

各ページは少なくとも1回リフローする必要があります。つまり、ページが初めてロードされるときに、レンダーツリーが構築されるため、この時点でリフローが発生する必要があります。ブラウザーは、リフロー中に、レンダリングツリーの影響を受ける部分を無効にし、レンダリングツリーのこの部分を再構築します。リフロー後、ブラウザーは影響を受ける部分を画面に再描画します。これを再描画と呼びます。

再描画

Render Treeの一部の要素が属性を更新する必要がある場合、これらの属性は要素の外観とスタイルにのみ影響し、背景色などのレイアウトには影響しません

リフローとリドローの関係

リフローは間違いなく再描画を引き起こし、再描画は必ずしもリフローを引き起こすわけではありません。

再描画とリフローを回避する2つの方法

1.逆流

  • ボックスモデル関連の属性は、再レイアウトとページリフローをトリガーします

幅高さパディングマージンディスプレイボーダー幅ボーダー最小高さ最小重量

  • 属性とフロートを配置すると、再レイアウトもトリガーされます

右上左下位置フロートクリア

  • ノードの内部テキスト構造を変更すると、中央レイアウトもトリガーされます

text-aligin over-flow-y font-wieghtオーバーフローfont-family line-height vertical-align white-space font-size

2.再描画

色border-style border-radius visvisibility text-tecoration background background-image background-position background-repeat background-size outline-color ouline outline-style outline-width box-shadow

 


たとえば
        、iQiyiのビデオを表示したり、このページのすべてのレイヤーをレイヤーで表示したり、レイヤー情報を表示したりすることができます。


再描画すると、ペイントが点滅し、再描画された要素が緑色でマークされます。たとえば、ビデオは再生中に再描画されました

新しいDOMを作成するプロセス

  1. DOMを取得した後、複数のレイヤーに分割
  2. 各レイヤーノードのスタイル結果を計算します(スタイルスタイルの再計算を再計算します)
  3. 各ノードのグラフィックスと位置を生成します(レイアウトリフローと再レイアウト)
  4. 各ノードをレイヤービットマップに塗りつぶします(Paint SteupおよびPaing-redraw)。
  5. レイヤーはテクスチャとしてGPUにアップロードされます
  6. 複数のレイヤーをページに結合して、最終的な画面イメージを生成します(複合レイヤー-レイヤーの再編成)

頻繁に再描画およびリフローされるDOM要素が独立したレイヤーとして使用される場合、DOM要素の再描画およびリフローの効果はこのレイヤーでのみ発生します。

DOM要素を新しい独立したレイヤーに変換する方法

Chromeでレイヤーを作成するための条件

  1. 3Dまたはパースペクティブ変換のCSS属性(パースペクティブ変換)
  2. 高速ビデオデコードには<video>ノードを使用
  3. 3D(WebGL)コンテキストまたは高速化された2Dコンテキストを持つCanvasノード
  4. フラッシュなどの混合プラグイン
  5. CSSを使用して独自の不透明度をアニメーション化するか、アニメーション化されたWebkitを使用して要素を変換します
  6. 高速化されたCSSフィルターを備えた要素
  7. 要素には、複合レイヤーを含む子孫ノードがあります(要素には、独自のレイヤーにある子要素があります)
  8. 要素には、zインデックスが低く、一致するレイヤーを含む兄弟要素があります(つまり、要素は複合レイヤーにレンダリングされます)

最適化ポイント

1.ブラウザの最適化メカニズム

最新のブラウザーは非常にスマートです。これは、並べ替えのたびに追加の計算消費が発生するため、ほとんどのブラウザーは変更をキューに入れてバッチ実行することにより、並べ替えプロセスを最適化します。ブラウザは変更操作をキューに入れ、一定期間が経過するか、操作がしきい値に達するまでキューをクリアしません。しかし!レイアウト情報の操作を取得すると、たとえば次のプロパティにアクセスしたり、次のメソッドを使用したりすると、キューが強制的に更新されます。

offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()getBoundingClientRect

上記の属性とメソッドは最新のレイアウト情報を返す必要があるため、ブラウザーはキューをクリアしてリフローをトリガーし、正しい値を返す必要があります。したがって、スタイルを変更する場合は、上記の属性の使用を避けるのが最善です。これらの属性はレンダリングキューを更新します。

これらの値を頻繁に取得して操作する場合は、最初にこれらの値をキャッシュできます。

2.最適化

属性値を取得するとリフローがトリガーされます。リフローにはキャッシュメカニズムがあるため、属性値を使用して読み取る場合、バッファーは強制的にリフレッシュされます。実際のデータを取得するため、バッファーメカニズムは無効になります。

レイヤーはパフォーマンスに影響し、レイヤーの作成に多くの時間を費やすため、レイヤーを悪用することはできません

  1. 親を介して子要素のスタイルを変更しないでください。子要素のスタイルを直接変更することをお勧めします。子要素のスタイルを変更しても、親要素と兄弟要素のサイズとサイズにはできるだけ影響を与えません。
  2. クラスを使用して要素のスタイルを設計し、スタイルの使用は避けてください。
  3. 複雑なアニメーション効果の場合、絶対配置を使用してドキュメントフローから分離します。そうしないと、親要素と後続の要素が大量にリフローされます。
  4. テーブルレイアウトを使用しないもう1つの理由は、テーブル内の要素がリフローをトリガーすると、テーブル内の他のすべての要素がリフローするようになるためです。適切なテーブルの場合、テーブルレイアウトを自動または固定に設定できます。
  5. インラインスタイルの複数のレイヤーを設定しないでください。
  6. 変更されたスタイルのcssTextを使用します。
  7. 再描画とリフローをトリガーするcss属性の使用を避けます
  8. 再描画とリフローの範囲を別のレイヤーに制限する
  9. トップの代わりに翻訳を使用
  10. 可視性を不透明度に置き換える
  11. DOMスタイルを1つずつ変更せず、クラスを事前定義してから、DOM classNameを変更してください。
  12. たとえば、domをオフラインで変更します。たとえば、最初に表示するDOMを指定します。なし(リフロー後)、次に100回変更してから表示します。
  13. ループ内のDOMノードの属性値を、offsetHeight、offsetWidthなどのループ内の変数として配置しないでください。
  14. CPUリソースを消費するため、アニメーションの実装速度を選択し、周波数を制限します
  15. アニメーションの場合、新しいレイヤーのビデオキャンバス変換プロパティ
  16. 位置変更を使用するときにwebGLなどのGPUアクセラレーションを有効にする

トップを使ってみませんか

トップはリフローをトリガーしますが、変換はそうしないため、例を挙げてください

1.トップを使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .text-box{
            position: absolute;
            left: 0;
            top: 0;
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
<div class="text-box">

</div>
<script>
    window.onload = () => {
        setTimeout(() => {
            let boxDomList = document.querySelectorAll('.text-box');
            boxDomList[0].style.top = '100px';
        }, 2000)
    }
</script>
</body>
</html>

2秒後にトップが100pxになることがわかります

第一段階:Reaclculateスタイル68 マイクロ秒

第二段階:レイアウトトリガーリフロー工程25 マイクロ秒

第3ステージ:レイヤーツリーの更新68μs

第4段階:ペイントの再描画23μs

第5段階:複合レイヤーの画像合成0.11ms

共有68 + 25 + 68 + 23 + 110 = 294μs

2.代わりに翻訳を使用する

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .text-box{
            /*position: absolute;*/
            /*left: 0;*/
            /*top: 0;*/
            transform: translateY(0);
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
<div class="text-box">

</div>
<script>
    window.onload = () => {
        setTimeout(() => {
            let boxDomList = document.querySelectorAll('.text-box');
            boxDomList[0].style.transform = 'translateY(100px)';
        }, 2000)
    }
</script>
</body>
</html>

レイアウトリフローなし

第一段階:Reaclculateスタイル80 マイクロ秒

第2段階:レイヤーツリーの更新60μs

第3ステージ:複合レイヤーの画像合成75μs

合計時間:80 + 60 + 75 = 215μs

初回と比較して、それほどではありませんが79μsを節約できますが、レイアウトが非常に複雑で、リフローが多い場合、ページには明らかなメリットがあります

使用シナリオ:ページフローティングウィンドウ

 

元の記事を5件公開 いいね!0 訪問数118

おすすめ

転載: blog.csdn.net/forteenBrother/article/details/105616725