JQuery each()関数はどのように循環DOM構造の性能を最適化するか

6296 ワード

jQueryというものは使うレベルにとどまり、具体的に実現するかどうか分からないと、本当に問題になりやすいです.これもなぜ最近私がjQueryをあまり推奨していないのか、このフレームワークのAPI設定は人々を道に迷わせる疑いがある.
 
  
$.fn.beautifyTable = function(options) {
// , options
return this.each(function() {
var table = $(this),
tbody = table.children('tbody'),
tr = tbody.children('tr'),
th = tbody.children('th'),
td = tbody.children('td');
// class
table.addClass(option.tableClass);
th.addClass(options.headerClass); //1
td.addClass(options.cellClass); //2
// class
tbody.children('tr:even').addClass(options.evenRowClass); //3
tbody.children('tr:odd').addClass(options.oddRowClass); //4
//
tr.children('th,td').css('text-align', options.align); //5
//
tr.bind('mouseover', addActiveClass); //6
tr.bind('mouseout', removeActiveClass); //7
//
tr.bind('click', toggleClickClass); //8
});
};

総じて言えば、このコードは悪くなくて、構想ははっきりしていて、論理は明確で、何をしたいのも注釈を通じてはっきり言いました.しかし、著者によると、表に120行ある場合、IEはスクリプトの実行時間が長すぎることを反映している.明らかに表現から見ると,この関数の効率は高くなく,極めて低下している.
そこで、コードの面から分析を始め、これは標準的なjQueryプラグイン式の関数であり、典型的なreturn thisがある.each(function( ) { ... };);形式的なコードは、著者がこのコードを書いたとき、本宣科のように考えていなければ、jQueryの関数が何をしたのかを意識しなければならない.
簡単に言えばjQuery.fnの下の関数は,ほとんどがeachの呼び出しであり,eachとは,自然に選択された要素を遍歴し,ある要素を指定した操作を行う.では、上のコードを見て、どれだけ遍歴したかを見てみましょう.ここでは120行しか選択されていません.各行に6列あり、1行のヘッダーを加えましょう.
thを巡回し、headerClassを追加し、要素数は6です.
tdを巡り、cellClassを追加します.要素数は6*120=720です.
すべてのtrから奇数を見つけるには、すべてのtrを1回遍歴する必要があります.要素数は120です.
奇数のtrを巡回し、evenRowClassを追加します.要素数は120/2=60です.
すべてのtrから偶数を見つけるには、すべてのtrを1回遍歴する必要があります.要素数は120です.
偶数のtrを巡回し、oddRowClassを追加します.要素数は120/2=60です.
すべてのthとtdを巡り、text-alignを追加し、要素数は120*6+6=726です.
すべてのtrを巡り、mouseoverイベントを追加します.要素数は120です.
すべてのtrを巡り、mouseoutイベントを追加します.要素数は120です.
すべてのtrを巡り、clickイベントを追加します.要素数は120です.
便宜上、ループで1つの要素にアクセスするのに10 msかかると簡単に仮定しますが、この関数は全部でどのくらいかかりますか?この関数は2172の要素に遭遇し、21720 ms、すなわち21秒かかり、IEはスクリプトの実行が長すぎることを明らかにした.
効率の低下の原因を知って、根本的に解決して、自然に方法を考えて循環を合併して、初めに少し見て、上のコードの中の注釈の中の数字に従って、少なくとも以下のいくつかの点は合併することができます:
3と4は1サイクルに統合でき、120+60+120+60から120になり、240が減少した.1、2および5は、6+720+726から726に変化し、726を減少させる1回のサイクルに統合することができる.6、7、8は1サイクルに統合でき、120+120+120から120になり、240が減少した.さらに,3,4は6,7,8と同様に1サイクルに統合でき,120を減少し続けた.加算すると、240+726+240+120=1326回の要素操作が合計13260 ms減少しました.最適化後,我々の関数は21720−13260=8460 ms,すなわち8 sの時間を費やした.
ここまで疑問があるかもしれませんが、表の構造的には、すべてのthとtd要素がtrの中にあるに違いありません.では、なぜ1、2、5の3つのステップのループをtrのループに同じように置いて、ネストされたループを形成しないのですか.
ここでそうしなかったのは、主に2つの原因があります.
1つは,1,2,5の3つをどこに置いても,すべてのthおよびtd要素への1回のアクセスを減らすことはできない.
一方,$(‘th,td’)というセレクタはsizzleで2回getElementsByTagName関数の呼び出しに翻訳され,1回目はすべてのthを取得し,2回目はすべてのtdを取得し,その後集合の集計を行う.getElementsByTagNameは内蔵関数であるため,この関数はループを持たない,すなわち複雑度がO(1)であり,同じ集合の集計にはArrayの相関関数を用い,メモリに対する操作であり,複雑度もO(1)であると考えられる.
逆に、tr要素に対するループで$('th,'td)というセレクタを再び採用すると、tr要素上でgetElementsByTagNameを2回呼び出し、どの要素上でこの関数を呼び出しても、関数の実行時間は同じであるため、trをループする際に使用され、119*2回の関数呼び出しが逆に多くなり、効率が低下しない.
sizzleセレクタの基本的な知識は、jQueryコードの最適化を助ける重要な側面でもあることがわかります.
javascriptに何もさせないでください.
前の基本的な最適化によれば,21秒から8秒に時間を落としたが,8秒という数字は明らかに受け入れられない.
さらに私たちのコードを分析すると、実際には、ループは言語レベルの内容であり、その速度はかなり速いはずです.各要素に対する操作はjQueryが提供する関数であり,遍歴に比べて大部分のリソースを占めるホストである.遍歴中に要素にアクセスするのに10 msを使う場合、addClassを1つ実行するのは少なくとも100 msレベルの消費です.
したがって,効率をさらに最適化するためには,要素に対する操作を減らすことから着手せざるを得ない.コードをさらに詳しくレビューすると、この関数には、少なくとも次のものが含まれているスタイルの変更が非常に多いことがわかります.
すべてのthにclassを加える.
すべてのtdにclassを追加します.
tr分パリティ行にclassを加える.
すべてのthとtdにtext-alignスタイルを追加します.
実際には、CSS自体が子セレクタを持っていることを知っていますが、ブラウザの元のCSSの解析は、javascriptに要素にclassを加えるよりもはるかに効率的です.
したがって、CSSが制御可能である場合、この関数はheaderClass、cellClassの2つの構成項目を持つべきではなく、できるだけCSSで構成する必要があります.
 
  
.beautiful-table th { /* headerClass */ }
.beautiful-table td { /* cellClass */ }

さらに、trのパリティ行スタイルについては、一部のブラウザでnth-child擬似クラスを使用して実現することができ、この点は特性プローブを利用して、この擬似クラスをサポートしていないブラウザでaddClass追加スタイルのみを使用することができる.もちろん、IEシリーズを最適化したいだけなら、これは無視できます.
:nth-child擬似クラスのプローブでは、stylesheetを作成し、#test span:nth-child(odd){display:block;}のようなルールを作成するという考え方で行うことができます.対応するHTML構造を作成し、idがtestのdivで、内部にspanが3つ配置されます.
stylesheetとdivを一緒に加えたDOMツリー.1番目と3番目のspanの実行期間displayスタイルを表示し、blockの場合、偽クラスがサポートされていることを示します.作成したstylesheetとdivを削除し、キャッシュプローブの結果を忘れないでください.最後に、すべてのthおよびtd要素にtext-alignスタイルを追加するにも、cssによって最適化することができる.どのalignが追加されているか分からない以上、いくつかのスタイルを書きます.
 
  
/* CSS */
.beautiful-table-center th,.beautiful-table-center td { text-align: center !important; }
.beautiful-table-rightright th,.beautiful-table-rightright td { text-align: rightright !important; }
.beautiful-table-left th,.beautiful-table-left td { text-align: left !important; }
/* javascript */
table.addClass('beautiful-table-' + options.align);

もちろん、上記の最適化は、CSSに対して制御権がある場合に確立されていますが、自分がCSSスタイルに触れることができなければ、例えばこれが汎用的なプラグイン関数であり、完全に制御できない第三者に使用される場合は、どうすればいいのでしょうか.まったく仕方がないわけではありません.
ページ内のすべてのCSSルール、例えばdocumentを探します.styleSheets.すべてのルールを巡り、コンフィギュレーション項目のheaderClass、cellClassなどを取り出します.必要ないくつかのclassのすべてのスタイルを抽出し、beautiful-table thのような新しいセレクタに自分で組み立てます.作成したセレクタを使用して、新しいstylesheetを生成し、DOMツリーに追加します.ではtableにbeautiful-tableというclassを付けるだけで済みます.
もちろん上のやり方も時間がかかりますが、結局stylesheetを巡り、stylesheetを作成します.具体的に効率の向上に役立つかどうかは、ページの規模によって異なる効果があり、使用するかどうかは関数設計者の具体的なニーズにかかっています.ここでは、戦略を提案します.
総じてjavascriptをできるだけ少なく実行し、より多くのスタイル化されたタスクをCSSに渡すことで、ブラウザのレンダリングエンジンが完了し、さらにこの関数を最適化することができ、addClass、cssの呼び出しに100 msが必要と仮定すると、今回の最適化は従来の120+726=846回の操作を直接消滅させることができる.84600 msの時間を節約しました(もちろん大げさな成分がありますが、関数全体の消費にとって、これは確かに大きな塊です).
この文章は、jQueryの各実現の面で最適化したいだけで、jQueryの全体の運行過程の分析、詳細の紹介と最適化の方向に関連しているだけで、いくつかの基本的な基本的な最適化方法には言及していません.例えば、table全体をDOMツリーから削除し、すべての操作を完了した後、DOMに戻し、repaintを減らします.mouseoverとmouseoutをmouseenterとmouseleaveに変更し、適切なイベントバブルモデルによる重複イベント関数の実行を低減します.th,tdのような単純な要素の選択については,オリジナルのgetElementsByTagNameを用いてsizzle解析セレクタを消滅させる時間を優先する.
最后に、この文章はただ说明したいだけで、フロントエンドの开発者に対して、ブラウザは暗い箱かもしれませんが、多くのフレームワーク、ツール、ライブラリはすべて开放的で、使用する前にある程度の理解を行うことができれば、必然的に个人の技术の向上と最终的な制品の品质の最適化に役立ち、