ではHTML
、各要素をボックスとして理解できます。ブラウザの解析プロセス中に、リフローと再描画が必要になります。
-
Reflow (リフロー): レイアウト エンジンは、さまざまなスタイルに従って、ページ上の各ボックスのサイズと位置を計算します。
-
再描画: ボックス モデルの位置、サイズ、およびその他の属性を計算した後、ブラウザーは各ボックスの特性に従って描画します。
特定のブラウザの解析およびレンダリング メカニズムは次のとおりです。
-
HTML の解析、DOM ツリーの生成、CSS の解析、CSSOM ツリーの生成
-
DOM ツリーと CSSOM ツリーを組み合わせて Render Tree を生成する
-
レイアウト(リフロー):生成されたレンダリングツリーに従ってリフロー(レイアウト)を行い、ノードの幾何情報(位置、サイズ)を取得します
-
塗りつぶし(再描画):レンダリングツリーとリフローで得た幾何情報をもとに、ノードの絶対ピクセルを求める
-
表示: ピクセルを GPU に送信し、ページに表示します。
ページの最初のレンダリング段階では、必然的にリフローがトリガーされます。これは、ページの最初に空白の要素として理解でき、後で新しい要素が追加されてページ レイアウトが変更されます。
変更の幾何学的サイズを変更する場合 (要素の幅、高さ、非表示要素などの変更など)、ブラウザは要素の幾何学的プロパティを再計算し、計算結果を描画する必要がありますDOM
。 DOM
要素の変更によってスタイル (または)DOM
が変更されるが、その幾何学的プロパティには影響しない場合 、ブラウザは要素の幾何学的プロパティを再計算する必要がなく、要素の新しいスタイルを直接描画します。再描画color
background-color
2. 発動方法
リフローと再描画の回数を減らすには、まずリフローと再描画がどのようにトリガーされるかを理解します
リフロートリガータイミング
リフローの段階は、主にノードの位置と幾何情報を計算することです。そのため、ページ レイアウトと幾何情報が変更されると、次のようにリフローが必要になります。
- 表示可能な DOM 要素を追加または削除する
- 要素の位置が変わる
- 要素のサイズの変更 (余白、内側の境界線、境界線のサイズ、高さと幅などを含む)
- テキストの変更や画像が異なるサイズの別の画像に置き換えられるなど、コンテンツの変更
- ページのレンダリングが開始されたとき (これは避けられません)
- ブラウザーのウィンドウ サイズが変更されます (リフローはビューポートのサイズに基づいて要素の位置とサイズを計算するため)
見過ごされやすい操作もいくつかあります: 特定の属性の値を取得する
offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
これらの属性には共通点が 1 つあります。つまり、その場で計算する必要があります。したがって、これらの値を取得するために、ブラウザもリフローします
方法以外はgetComputedStyle
原理は同じ
再描画トリガーのタイミング
リフローをトリガーすると、間違いなく再描画がトリガーされます
ページは黒板として理解でき、黒板には小さな花が描かれています。次に、この花を左から右に移動したいので、右側の特定の位置を決定し、形状を描画 (リフロー) してから、元の色をペイント (再描画) する必要があります。
さらに、他にも再描画動作がいくつかあります。
-
色の変更
-
テキストの向きの変更
-
影の修正
ブラウザの最適化メカニズム
再配置のたびに追加の計算消費が発生するため、ほとんどのブラウザーは、変更をキューに入れ、それらをバッチで実行することにより、再配置プロセスを最適化します。ブラウザーは変更操作をキューに入れます。キューは、一定期間または操作がしきい値に達するまで空になりません。
レイアウト情報の操作を取得すると、キューが強制的に更新され、上記のメソッドを含めてoffsetTop
最新のデータが返されます
そのため、ブラウザーはキューをクリアし、リフロー再描画をトリガーして正しい値を返す必要があります
3. 削減方法
リフローをトリガーしてシーンを再描画する方法を学びました。リフローを回避した経験を以下に示します。
- 要素のスタイルを設定する場合は、要素のクラス名を変更して
class
(可能な限り DOM ツリーの最内層で) - 複数のインライン スタイルを設定しない
- 値または
position
属性の 値を使用して、要素のアニメーションを適用します (前の例で説明したように)。fixed
absolute
table
レイアウトの使用は避けてください 。table
変更内の各要素のサイズと内容によって、全体が再table
計算されます。position: fixed/absolute
これらの複雑なアニメーションの場合は、他の要素への影響を減らすために、要素を可能な限りドキュメント フローから除外するように設定します。- css3 ハードウェア アクセラレーションを使用すると、
transform
、opacity
、およびfilters
これらのアニメーションでリフローの再描画が発生しないようにすることができます - CSS
JavaScript
式を避ける
複数のノードの動的挿入を使用する場合に JavaScript
使用できますDocumentFragment
. 一度作成すると、一度挿入できます. 複数のレンダリングのパフォーマンスを回避できます.
しかし、必然的にリフローや再描画を行うこともありますが、それらをより有効に活用できます。
1. スタイルの変更を避け、クラス名を使用してスタイルをマージします。
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
への変更:
<style>
.basic_style {
width: 100px;
height: 200px;
border: 10px solid red;
color: red;
}
</style>
<script>
const container = document.getElementById('container')
container.classList.add('basic_style')
</script>
前者は、個別に操作するたびにレンダリング ツリーの変更をトリガーします (新しいブラウザーではそうではありません)。
レンダリング ツリーの変更をトリガーし、対応するリフローおよび再描画プロセスを実行する
マージ後、すべての変更を一度に送信することを意味します
2. DOM を「オフライン」にする
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
...(省略了许多类似的后续操作)
への変更:
let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
...(省略了许多类似的后续操作)
container.style.display = 'block'
一部の学生は、要素を削除して元に戻すと、これもコストのかかるリフローを引き起こしませんか?と尋ねることがあります。これは本当ですが、削除しました. 今後この要素を何度操作しても、各ステップの操作コストは非常に低くなります. 少数の DOM 操作しか必要ない場合、DOM オフラインの利点は実際には明らかではありません。操作が頻繁になると、「削除」と「元に戻す」のオーバーヘッドは非常に価値のあるものになります。
参照: