ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript に最適化された DOM

JavaScript に最適化された DOM

php中世界最好的语言
php中世界最好的语言オリジナル
2018-03-19 16:27:091870ブラウズ

今回は、DOM を最適化するための JavaScript と、DOM を最適化するための JavaScript の 注意事項 について、実際の事例を紹介します。

DOM の最適化は再描画とリフローから始める必要があります。大昔...

1. 再描画とリフロー

1.1 再描画とリフローとは

再描画とは、いくつかのスタイルの変更を指しますが、要素の位置とサイズは変更されていません。

再配置とは、要素の位置またはサイズが変更され、新しいレンダリング ツリーが確立された後、ブラウザがレンダリング ツリーを再計算する必要があることを意味します。再描画されます。

1.2 ブラウザレンダリングページ

面接に行くときは必ず尋ねられる質問があります。それは、「ブラウザに 1 行の URL を入力するとどうなりますか?」という質問です。ネットワークの知識へ これには、ブラウザのページレンダリングの問題も関係します。ブラウザはサーバーから応答されたページを受信すると、CSS に遭遇すると 1 行ずつレンダリングを開始し、解析が完了した後、DOM ツリーの解析を続けます。計算されたスタイル (スタイル ボックス) は DOM ツリーと結合されてレンダリング ツリーを形成し、ブラウザによってページ上に描画されます。 DOM ツリーとレンダー ツリーの違いは、display:none; スタイルを持つノードは DOM ツリーには存在しますが、レンダー ツリーには存在しないことです。 描画後、ブラウザはjsファイルの解析を開始し、jsに基づいて再描画とリフローを行うかどうかを決定します。

1.3 再描画とリフローの理由

再描画を引き起こす要因:

  • 要素のサイズ、位置などを変更せずに、

    可視性、輪郭、背景色、その他のスタイル属性を変更する。ブラウザは要素の新しいプロパティに基づいて再描画します。

リフローを引き起こす要因:

    コンテンツの変更
    • テキストの変更または画像サイズの変更
    DOM要素の幾何学的プロパティの変更
    • 例えば、 DOM の変更 要素の幅と高さが変更されると、元のレンダリング ツリー内の関連ノードが無効になり、ブラウザは変更された DOM に基づいてレンダリング ツリー内の関連ノードを再配置します。親ノードの幾何学的属性が変更されると、その子ノードと後続の兄弟ノードの位置が再計算され、一連の再配置が発生します。
    DOM ツリーの構造変更
    • DOM ノード

      の追加、DOM ノードの位置の変更、およびノー​​ドの削除はすべて DOM ツリーに対する変更であり、ページの再配置が発生します。ブラウザのレイアウトは上から下のプロセスです。現在の要素を変更しても、その前に移動された要素には影響しません。ただし、新しい要素がすべてのノードの前に追加されると、後続の要素はすべて再配置されます。

    いくつかの属性の取得
    • レンダリング ツリーでの直接の変更に加えて、いくつかの属性値を取得するときにブラウザも再配置され、これらの属性には次のものが含まれます。
  • ブラウザウィンドウサイズの変更

    • ウィンドウサイズの変更は、Webページ全体内の要素のサイズの変更、つまりDOM要素のコレクション属性の変更に影響を与え、再配置を引き起こします。

  • スクロールバーの外観 (ページ全体のリフローをトリガーします)

つまり、js はシングルスレッドであり、再描画とリフローはユーザーの操作をブロックし、 Web ページのパフォーマンスが低下し、ページ要素の幅と高さを 500 ミリ秒ごとに変更するタイマーを作成した場合など、ページが複数回再描画され、リフローされると、ページがますますスタックする可能性があります。可能な限りリフローします。 DOM の最適化もこの出発点に基づいています。

2. 最適化

2.1 アクセスを減らす

アクセス数を減らすということは当然要素をキャッシュすることを意味しますが、注意してください

var ele = document.getElementById('ele');

これは ele を呼び出すたびに ele に 1 回アクセスすることと同じになります。 . エレのノードです。

2.1.1 NodeListのキャッシュ

var foods = document.getElementsByClassName('food');

foods[i]を使用してクラスfoodのi番目の要素にアクセスできますが、ここでのfoodsは配列ではなくNodeListです。 NodeList は、いくつかの順序付けされたノードを格納する配列のような配列で、位置によってこれらのノードにアクセスできます。 NodeList オブジェクトは動的であり、アクセスごとにドキュメントベースのクエリが実行されます。したがって、NodeList へのアクセス数を最小限に抑える必要があり、NodeList の値をキャッシュすることを検討できます。

// 优化前var lis = document.getElementsByTagName('li');for(var i = 0; i < lis.length; i++) {     // do something...  }// 优化后,将length的值缓存起来就不会每次都去查询length的值var lis = document.getElementsByTagName('li');for(var i = 0, len = lis.length; i < len; i++) {     // do something...  }

また、NodeListは動的に変化するため、キャッシュしておかないとNodeListの長さを取得する際に要素を追加するなどの無限ループが発生する可能性があります。

2.1.2 セレクターを変更する

要素を取得するには、getElementsByXXX() と queryselectorAll() の 2 つの最も一般的な方法があります。これら 2 つのセレクターの違いは非常に大きく、前者は動的コレクションを取得することです。たとえば、静的コレクションを取得します。

// 假设一开始有2个livar lis = document.getElementsByTagName('li');  // 动态集合var ul = document.getElementsByTagName('ul')[0]; 
for(var i = 0; i < 3; i++) {
    console.log(lis.length);    var newLi = document.createElement('li'); 
    ul.appendChild(newLi);
}// 输出结果:2, 3, 4var lis = document.querySelector('li');  // 静态集合 var ul = document.getElementsByTagName('ul')[0]; 
for(var i = 0; i < 3; i++) {
    console.log(lis.length);    var newLi = document.createElement('li'); 
    ul.appendChild(newLi);
}// 输出结果:2, 2, 2

静的コレクションに対する操作ではドキュメントの再クエリが発生せず、動的コレクションよりも最適化されています。

2.1.3 不要なループを避ける

// 优化前
for(var i = 0; i < 10; i++) {
    document.getElementById('ele').innerHTML += 'a';
} 
// 优化后 
var str = ''; 
for(var i = 0; i < 10; i++) {
    str += 'a'; 
}
document.getElementById('ele').innerHTML = str;

最適化前のコードは ele 要素に 10 回アクセスしますが、最適化されたコードは ele 要素に 1 回しかアクセスしないため、効率が大幅に向上します。

2.1.4 イベントデリゲート

js のイベント関数はすべてオブジェクトです。イベント関数が多すぎると、より多くの DOM 要素がイベントにバインドされ、より多くのメモリを占有します。にアクセスすると、ページのインタラクティブな準備時間も長くなります。そこで、イベント委任が誕生しました。イベント委任は、イベント バブリングを利用します。特定の種類のすべてのイベントを管理するには、1 つの イベント ハンドラー プログラムのみを指定できます。

// 事件委托前var lis = document.getElementsByTagName('li');for(var i = 0; i < lis.length; i++) {
   lis[i].onclick = function() {
      console.log(this.innerHTML);
   };  
}    
// 事件委托后var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(event) {
   console.log(event.target.innerHTML);
};

イベント委任の前に lis.length 回 li を訪問しましたが、イベント委任を使用した後は ul を 1 回だけ訪問しました。

2.2 再描画と再配置を減らす

2.2.1 domノードの複数のスタイルを変更する

p要素の幅と高さを変更したいのですが、通常のアプローチは次のようになります

 p = document.getElementById('p1'

。上記の操作では、要素の 2 つの属性が変更され、DOM が 3 回アクセスされ、2 回の再配置と 2 回の再描画がトリガーされました。最適化とはアクセス数と再描画と再順序の数を減らすことであると述べましたが、この出発点から、要素に 1 回アクセスするだけで再順序の数を 1 に減らすことができるでしょうか。もちろん可能です。css でクラスを作成できます

/* css
.change {
    width: 220px;
    height: 300px;
}*/document.getElementById('p').className = 'change';

この方法で、一度に複数のスタイルを操作できます

2.2.2 dom ノードのスタイルをバッチで変更する

上記のコードは 1 つの dom ノード用です。 DOM コレクションのスタイルを変更したい場合は?

最初に思いつく方法は、コレクションを走査し、各ノードに className を追加することです。もう一度考えてみてください。これは、dom ノードに何度もアクセスしていることを意味しませんか? DOM ツリーと記事の冒頭で述べたレンダリング ツリーの違いを考えてください。ノードの表示属性が none の場合、このノードはレンダリング ツリーに存在しません。つまり、このノードの操作は行われません。レンダー ツリーには影響せず、再描画や再配置も発生しません。このアイデアに基づいて最適化を実現できます:

  • 表示: なし;

    変更されるコレクションの親要素
  • 之后遍历修改集合节点

  • 将集合父元素display: block;

// 假设增加的class为.changevar lis = document.getElementsByTagName('li');  
var ul = document.getElementsByTagName('ul')[0];
ul.style.display = 'none';for(var i = 0; i < lis.length; i++) {
    lis[i].className = 'change';  
}
ul.style.display = 'block';

3、总结

  • 减少访问dom的次数

    • 缓存节点属性值

    • 选择器的使用

    • 避免不必要的循环

    • 事件委托

  • 减少重绘与重排

    • 使用className改变多个样式

    • 使父元素脱离文档流再恢复

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Vue的计算属性

Webpack模块的使用

以上がJavaScript に最適化された DOMの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。