並べ替えと描き直し


まず、前の文の高性能JavaScript DOMプログラミングを振り返ってみると、主に2つの最適化を提示しました.一つはDOMのアクセスをできるだけ減らすことです.一つはECMAScriptの端に演算を置いて、二つはできるだけローカル変数をキャッシュすることです.例えば、lengthなど、最後に二つの新しいAPIを紹介しました.  querySelector()及びquerySelectorAll()は、組み合わせ選択をする時に大胆に使用することができる.本論文では主にDOMプログラミングが最も時間がかかるところで、並べ替えと描き直しをします.
1、何が並べ替えと描き直しですか?
ブラウザは、ページ内のすべてのコンポーネントをダウンロードしました.HTMLマーク、JavaScript、CSS、画像の後に解析的に2つの内部データ構造を生成します.
DOMツリーはページ構造を表し、ツリーはDOMノードがどのように表示するかを示しています.DOMツリーのそれぞれの表示が必要なノードは、レンダリングツリーに少なくとも1つの対応するノードが存在する(隠れたDOM要素disply値はnoneであり、レンダリングツリーには対応するノードがない).レンダリングツリーのノードを「フレーム」または「ボックス」と呼び、CSSモデルの定義に符合して、ページ要素がパディング、エッジ、枠と位置を持つ箱であることを理解し、DOMとレンダリングツリーの構築が完了すると、ブラウザはページ要素の表示(描画)を開始する.
DOMの変化が元素の幾何学的性質(幅または高さ)に影響を及ぼすとき.ブラウザで要素の幾何学的属性を再計算する必要があります.他の要素の幾何学的属性や位置も影響されます.ブラウザは、ツリーに影響を与えた部分を無効にして、レンダリングツリーを再構築します.このプロセスを並べ替えといいます.並べ替えが完了すると、ビューアは影響を受けた部分を画面に再描画します.このプロセスをリピートといいます.ブラウザのためです.フローレイアウトは、レンダリングツリーに対する計算は通常1回で完了することができます.しかし、テーブルと内部要素は除外されています.レンダリングツリーにおけるノードの属性を確認するには、何度も計算しなければならないかもしれません.通常は同じ要素の3倍の時間がかかります.これは、テーブルを使用してレイアウトをすることを避けるためにもあります.
すべてのDOM変化は幾何学的性質に影響するわけではなく、例えば元素の背景色を変えることは元素の幅と高さに影響しない.
2、並べ替えと描き直しの価格はどれぐらいですか?
リフォームとリフォームの価格はどれぐらいですか?また、前の橋を渡る例に戻ります.注意深いあなたは気づくかもしれません.千倍の時間差は「橋を渡る」手によるものではないです.毎回「橋を渡る」というのは、並べ替えと描き直しを伴っています.エネルギーを消耗する大きな部分もここにあります.
var times = 15000;

// code1     +  +  
console.time(1);
for(var i = 0; i < times; i++) {
  document.getElementById('myDiv1').innerHTML += 'a';
}
console.timeEnd(1);

// code2    
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
  var tmp = document.getElementById('myDiv2').innerHTML;
  str += 'a';
}
document.getElementById('myDiv2').innerHTML = str;
console.timeEnd(2);

// code3 
console.time(3);
var _str = '';
for(var i = 0; i < times; i++) {
  _str += 'a';
}
document.getElementById('myDiv3').innerHTML = _str;
console.timeEnd(3);


// 1: 2874.619ms
// 2: 11.154ms
// 3: 1.282ms
データは嘘つかないです.見ましたよね.何度もDOMを訪問するのは、並べ替えや描き直しにとって時間の無駄です.
3、並び替えはいつ発生しますか?
毎回並べ替えをすると、必ず描き直しが起こります.それでは、並べ替えはどのような状況で発生しますか?
  • 可視DOM要素
  • を追加または削除する.
  • 元素位置変更
  • 元素サイズ変更
  • 要素のコンテンツ変更(例えば、テキストが別のサイズの画像によって代替される)
  • ページレンダリング初期化(これは避けられない)
  • ブラウザのウィンドウサイズ変更
  • これらはすべて明らかで、もしかするとあなたはすでにこのような体得があったかも知れなくて、間断なくブラウザのウィンドウの大きさを変えて、UIの反応が鈍くなる(いくつかの低いバージョンのIEの下で直接切ってしまうかもしれません)を招いて、今あなたははっと悟ったかもしれなくて、間違いなくて、毎回の再配置の重さが絵を描くのがもたらしたのです!
    4、レンダリングツリーの変化の行列とリフレッシュ
    次のコードを考える:
    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    ele.style.padding = '5px';
    一度に考えてみると、元素のデザインは三回も変えています.毎回変更すると、並べ替えと描き直しを引き起こします.だから、全部で三回の並べ替えがあります.しかし、ブラウザはこんなに馬鹿ではありません.これは三回の修正を「保存」します.(常に知らず知らず)キューの更新を強制し、計画タスクの即時実行を要求します.レイアウト情報を取得する操作は、キューの更新をもたらします.
  • offset Top,offset Left,offset Width,offset Height
  • scrollTop、scrollLeft、scrollwidth、scrollHeight
  • clientTop、clientLeft、client Width、clientHeight
  • get ComputatidStyle()
  • 上のコードを少し修正します.
    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    
    // here use offsetHeight
    // ...
    ele.style.padding = '5px';
    offset Height属性は最新のレイアウト情報を返す必要があるので、ブラウザはレンダリングキューの「処理待ちの変化」を実行し、正しい値に戻るように並べ替えをトリガします.上記のコードは、前回の操作ではレンダリングキューにキャッシュされて処理されますが、一旦offset Height属性が要求されると、キューはすぐ実行されますので、全部で二回の並べ替えと描き直しがあります.
    5、並べ替えと描き直しを最小化する
    私達はやはり上のコードを見ます.
    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    ele.style.padding = '5px';
    3つのスタイルの属性が変更され、各要素の幾何学的構造に影響を与えます.ほとんどの現代ブラウザは最適化されていますが、並べ替えを引き起こすだけです.しかし、上述のように、タイムリーな属性が要求されると、強制的に列を更新します.そして、このコードは4回にわたってDOMにアクセスします.明らかな最適化戦略はそれらの操作を合成します.このようにDOMを一回だけ修正します.
    var ele = document.getElementById('myDiv');
    
    // 1.   style
    ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
    
    // 2. add style
    ele.style.cssText += 'border-;eft: 1px;'
    
    // 3. use class
    ele.className = 'active';
    6、fragment元素の応用
    次のコードを見て、問題を考えます.
    • apple
    • orange
    コードにpeach、watermelonの二つのオプションを追加するとどうなりますか?
    var lis = document.getElementById('fruit');
    var li = document.createElement('li');
    li.innerHTML = 'apple';
    lis.appendChild(li);
    
    var li = document.createElement('li');
    li.innerHTML = 'watermelon';
    lis.appendChild(li);
    上記のコードは容易に考えられますが、明らかに二回並びました.どうやって破れますか?前に言ったように、隠れた要素はレンダリングツリーの中にないです.素晴らしいです.私達は先にidをflutのul元素として隠してもいいです.(display=none)li要素を追加して最後に表示しますが、実際の操作でフラッシュが発生する可能性があります.これも分かりやすいです.
    var fragment = document.createDocumentFragment();
    
    var li = document.createElement('li');
    li.innerHTML = 'apple';
    fragment.appendChild(li);
    
    var li = document.createElement('li');
    li.innerHTML = 'watermelon';
    fragment.appendChild(li);
    
    document.getElementById('fruit').appendChild(fragment);
    ドキュメントセグメントは、軽量級のdocumentオブジェクトであり、このようなタスクを達成するために設計されたものである.更新とモバイルノード.ドキュメントセグメントの便利な構文特性は、ノードにクリップを追加すると、実際にはセグメント自体ではなくセグメントのサブノードが追加されます.再配置をトリガし、リアルタイムのDOMのみを訪問しました.
    7、要素をアニメから外す
    一部のページを展開/折りたたみで表示/非表示にするのはよくあるインタラクションモードです.通常は展開領域の幾何学アニメーションを含み、ページの他の部分を下に押し下げます.
    一般に、並べ替えはレンダリングツリーの一部にしか影響しませんが、レンダリングツリー全体にも影響が大きいかもしれません.ブラウザの並び替えが必要な回数が少ないほど、アプリケーションの応答速度が速くなります.したがって、ページ上部のアニメーションの一つがページ全体の残りの部分を推移させると、一度の価格が高い大規模な並べ替えにつながり、ユーザーにページの大きさを感じさせます.レンダリングツリーで再計算が必要なノードが多ければ多いほど、状況が悪くなります.
    次のステップを使用すると、ページの大部分の並べ替えが回避されます.
  • は、絶対位置特定ページ上のアニメーション要素を使用して、文書フロー
  • から逸脱する.
  • は要素を動かします.拡大すると、部分ページを一時的にカバーします.しかし、これはページの小さな領域の再描画プロセスにすぎません.ページの大部分を並べ替えることはできません.
  • アニメーションが終了すると、位置が回復し、ドキュメントの他の要素だけが下に移動します.
    8、まとめ
    並べ替えと描き直しはDOMプログラミングにおける消費エネルギーの主な原因の一つです.普段はDOMプログラミングに関連していますが、以下のいくつかの点を参考にしてもいいです.
  • レイアウト情報が変更されたときにクエリーを行わないようにする(レンダリングキューの強制更新につながる)
  • 同じDOMの複数の属性変更は、一緒に書くことができます(DOMアクセスを低減しながら、強制レンダリングキューのリフレッシュのリスクを0に低減します).
  • 大量にDOMを追加する場合、まず要素をドキュメントフローから逸脱させて、操作が終わったら文書フローに持ち込んでもいいです.これは一回の並べ替えをトリガするだけです.
  • は、複数回の並べ替えが必要な要素であり、position属性はabsoluteまたはfixedとして設定され、この要素は文書フローから離れています.その変化は他の要素に影響を与えません.例えば、アニメーション効果のある要素は絶対位置に設定したほうがいいです.