どのようにJavaScript操作DOMの効率を高めますか?

7424 ワード

NichollasはJavaScriptの操作DOMの効率を高める方法を説明します.
Web開発ではJavaScriptの重要な役割はDOMを操作することですが、ご存知ですか?DOMの操作は非常に高価です.これはブラウザがリフロー操作を実行することにつながるので、多すぎるリフロー操作を実行すると、自分のウェブサイトがだんだん遅くなっていることが分かります.できるだけDOM操作を減らすべきです.本論文はこのシリーズの最後の編であり、いつDOMに対してどのような操作ができるかなどの指導原則を提示した.
【原文】Nichollas C.Zakas-Speed up your JavaScript,Part 4
【原文】事理に明るい
過去数週間にわたって、JavaScriptスクリプトの動作速度を速くする技術をいくつか紹介しました.
第1節
サイクルを最適化する方法を紹介しました.
第二節
最適化関数の内部コードに重点を置いて、キューとメモリ化の2つの技術を紹介して、関数の作業負担を軽減します.
第3節
再帰を反復または記憶化の方式に変換する方法について議論を始めた.第四節はこのシリーズの最後の編であり、つまり本稿では、多すぎるDOM操作による影響を重点的に述べる.
 
DOM動作の効率は低く,一般的に遅いのではなく,性能問題を引き起こす一般的な問題の一つであることを知っている.なぜ遅いですか?DOMに対する修正はウェブページのユーザーインターフェースに影響するため、再描画ページは高価な操作である.多すぎるDOM操作は一連の再描画動作をもたらし、実行結果の正確性を確保するために、すべての修正動作は順次同期して実行される.私たちはこのプロセスを回流(reflow)と呼びます.これも最も高価なブラウザ操作の一つです.リフロー演算は主にいくつかの場合に発生します.
 
*DOMノードに対して新規または削除操作を行う場合.
 
*動的にスタイルを設定する場合(element.style.width=「10 px」など).
 
*計算されなければならないサイズ値を取得すると、例えば、offset Width、clientHeight、または他の計算されたCSS値にアクセスする(DOM対応のブラウザでは、get ComputedStyle関数で取得することができ、IEでは、currentStyle属性で取得することができる).
 
問題解決の鍵は、DOM操作によって引き起こされるリターンの回数を制限することです.ほとんどのブラウザはJavaScriptの実行中にDOMを更新しません.したがって、これらのブラウザはDOMに対する操作をキューに入れ、JavaScriptスクリプトの実行が完了したら、順番に一回実行します.つまり、JavaScriptが実行する過程で、ユーザはブラウザと対話することができなくなり、リフロー操作が実行されるまでになる.(コントロールダイアログは、JavaScriptの実行を中止する動作を実行したので、ユーザインターフェースを更新するためのリフロー動作をトリガする)
 
DOM修正によるリフロー動作を低減するには二つの基本的な方法がある.一つ目は、現在のDOMを操作する前に、できるだけ多くの準備をすることです.古典的な例としては、documentオブジェクトに多くのDOMノードを追加することです.
 
/*
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
*/
このコードの効率は、彼がサイクル毎に現在のDOM構造を修正するため、非常に低い.性能を向上させるためには、この回数を最低にする必要があります.このケースでは、ドキュメントの断片を作成し、要素を作成した一時的な容器として最後に容器の内容を直接親ノードに追加するのが一番いいです.
 
/*
var fragment = document.createDocumentFragment();
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
fragment.appendChild(item);
}
list.appendChild(fragment);
*/
調整されたコードは、一度だけ現在のDOMの構造を変更します.最後の行にあります.その前に、私たちはドキュメントの破片で中間結果を保存します.このような変更は、ドキュメントの破片には可視性がないため、リフロー動作を開始しません.実際には、ドキュメントの断片もDOMに追加できません.パラメータとしてapendChild関数に渡す必要があります.実際には、ドキュメントの断片自体ではなく、下のすべてのサブ要素が追加されています.
 
バックフロー動作を必要としないようにする別の方法は、DOMを操作する前に、動作する要素を現在のDOM構造から削除することである.要素を削除するには、基本的に2つの方法があります.
 
1.RemoveChild()またはreplacceChild()によって本当の意味での削除を実現する.
 
2.この要素を設定するdisplayスタイルは「none」です.
 
修正操作が完了すると、上記のプロセスは反転して、削除された要素を現在のDOM構造に追加します.やはり上の例を説明します.
 
/*
list.style.display = "none";
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
list.style.display = "";
*/
リストのdisplayスタイルを「none」に設定すると、この要素は現在のDOM構造から削除されます.このノードはもう見られないからです.display属性を元のデフォルト値に戻す前に、その下にサブ要素を追加すると、リフロー動作がトリガされません.
 
もう一つは、しばしばバックフロー動作を引き起こす場合、スタイル属性によって要素の外観を修正することである.例えば、次のような例があります.
 
/*
element.style.backgroundColor = "blue";
element.style.color = "red";
element.style.fontSize = "12em";
*/
このコードは3つのスタイルを修正し、同時に3回のリフロー動作をトリガします.要素のスタイル属性を変更するたびに、リフロー動作が起動されます.要素の多くのスタイルを同時に修正するには、これらのスタイルを一つのクラスに置いて、要素のクラスを直接修正するのが一番いいです.これは単独で要素のスタイルを修正するよりもはるかに強いです.例えば、次のような例があります.
 
/*
.newStyle {
background-color: blue;
color: red;
font-size: 12em;
}
*/
このように私たちはJavaScriptコードの中で、この行のコードだけでスタイルを変更できます.
 
/*
element.className = "newStyle";
*/
要素のクラスの属性を変更すると、すべてのスタイルがターゲット要素に適用され、1回だけループ操作が開始されます.このようにするのはもっと効果的ではなくて、その上更にメンテナンスしやすい.
 
DOMはほとんどすべての場合に遅いので、取得したDOMデータをキャッシュする必要がある.この方法は、一般的な場合においても同様に適用されるように、リフロー動作をトリガする属性(例えば、offset Widthなど)の取得に特に重要である.効率の悪い大仰な例を紹介します.
 
/*
document.getElementById("myDiv").style.left = document.getElementById("myDiv").offsetLeft +
document.getElementById("myDiv").offsetWidth + "px";
*/
ここではgetElementById()に対して3回呼び出しました.大きな問題です.DOMにアクセスするのはとても高価です.この3つの呼び出しはちょうど同じ要素です.私たちは以下のように書くといいかもしれません.
 
/*
var myDiv = document.getElementById("myDiv");
myDiv.style.left = myDiv.offsetLeft + myDiv.offsetWidth + "px";
*/
いくつかの冗長操作を除去しましたが、今はDOM操作の回数が減少しました.使用回数が一回を超えるDOM値に対しては、バッファリングして無意味な性能消費を避けることができます.
 
属性の遅いアクセス速度の張本人はHTMLCollectionオブジェクトかもしれません.これらのオブジェクトはobjectタイプで、DOMがノードのセットに戻る必要があるときにこのオブジェクトを使用します.つまり、childNodes属性とgetElements ByTagName()の戻り値はこの場合に属します.私たちはよくHTMLCollectionを配列として使用しますが、彼はDOM構造によって自動的に変化するエンティティオブジェクトです.あなたがHTMLCollectionオブジェクトの属性を訪問するたびに、彼はDOM内のすべてのノードに完全にマッチングします.これは次のコードがデッドサイクルを引き起こすことを意味します.
 
/*
var divs = document.getElementsByTagName("div");
for (var i=0; i < divs.length; i++){ //infinite loop
document.body.appendChild(document.createElement("div"));
}
*/
このコードはなぜ死循環になったのですか?毎回の循環において、div要素がdocumentに追加され、divsという集合が更新されるからです.つまり、循環インデックスはdivs.lengthの値を永遠に超えないです.divs.lengthの値はサイクルとともに増加します.divs.lengthにアクセスするたびに、通常の配列にアクセスするlength属性よりも大きな対価を支払うことができるセットオブジェクトを更新する.HTMLCollectionオブジェクトを操作する場合、訪問回数をできるだけ最低にするべきです.最も簡単なのは、length属性をローカル変数にキャッシュすることで、サイクルの効率を大幅に高めることができます.
 
/*
var divs = document.getElementsByTagName("div");
for (var i=0, len=divs.length; i < len; i++){ //not an infinite loop
document.body.appendChild(document.createElement("div"));
}
*/
修正後のコードはもうデッドサイクルではないです.サイクル毎にlenの値は固定されています.属性値をキャッシュすることは、より効率的であるだけでなく、複数のクエリを実行しないことが保証されます.
 
本論文は「Speed up your JavaScript」シリーズの最後の文章です.脚本が暴走しないようにするためのダイアログボックスと、どのようにスクリプトをより速く実行するかを今から知ってほしいです.私が言っている技術は他の人がたくさん言っています.私はそれらを一つにまとめるだけです.そうすると、みんながもっと簡単にこれらの情報を見つけることができます.何かもっといい話題があったら整理してください.コメントの中で直接教えてください.原文の住所:http://www.v-ec.com/dh20156/article.asp?id=216
//lt;[CDATA[
if($=jQuery){
ドル=jQuery.noConflick()
)
var isLogied=true;
var cbblogId=8949;
var cb铅ntryId=1594310;
var cbbblogApp="ghost 527"
var cbbagublogUserGrid=「2 e 93310 b-63 cf-dd 11-9 e 4 d-001 cf 0 c d 104 b」
var cbgantry CreatedDate='2009/11/2 9:36:00'
//gt;