私は頻繁にjQueryのDOM操作のパフォーマンスが良くありません文句を言うと、多くの場合、最適化する方法のいくつかを使用しようとするが、より最適化された、jQueryのは、すでに非常によくやっていることを見つけるために、より落胆、最適化があまりにもユーザーの視点から行うことができます(のみ外部から最適化するために介入することができない、それは比較的閉じたライブラリであると言うのに対し、これは、jQueryの性能が優れているというわけではありません)限ら。この記事では、障害の最適化の経験を記録します。
最適化のアイデア
今回は、データベースから最適化のアイデア。データベースの最適化は、私たちはしばしば言うとき、「一緒に提出したトランザクションで操作を多数、効率を向上させることができます。」データベースは理解していないが、私はなぜ知っているが、「業務」の思想が、私は(......間違っているが)方向を指摘したようにしないでください。
だから私は、「開く」とトランザクション、外部からのjQueryのいくつかの最適化を「送信」によるjQueryのへの「取引」の概念を導入しようとした、最も重要なのは、サイクル数各機能を削減することです。
我々はすべて知っているように、jQueryのDOM操作は、すべてを取得するには、オペレーティングDOM属性/スタイルを設定するために使用される標準的な、と最初に設定し、ほぼすべての次の反復の選択された要素の、jQuery.access機能は、コア部分の一つであり、次のように前記コードを循環させるための手段です。
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn(
elems[i],
key,
exec ? value.call(elems[i], i, fn(elems[i], key)) : value,
pass
);
}
return elems;
}
例えばjQuery.fn.css機能はこれです:
jQuery.fn.css = function( name, value ) {
// Setting 'undefined' is a no-op
if ( arguments.length === 2 && value === undefined ) {
return this;
}
return jQuery.access( this, name, value, true, function( elem, name, value ) {
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
});
};
したがって、このコードはdiv要素5000を選択することが想定され、次の、万個のノードがアクセス・サイクルを持つことになります。
jQuery('div').css('height', 300).css('width', 200);
私のシナリオでは、「トランザクション」は、データベースのように一般的な操作することができ、すべての操作を保存することで、「トランザクションのコミット」の時間に統一し、5000と同等に減少万個のノード、へのアクセス「ONE」の性能を向上させます。
単純な実装
jQueryの操作式の「取引」、二つの機能を提供します:
- 開始:「トランザクション」を開き、それはトランザクションのオブジェクトを返します。このオブジェクトは、jQueryのすべての機能を持っていますが、有効にするだけで、「Submitを取引」の後、すぐには反映されません関数を呼び出します。
- コミット:機能の上にすべての以前の呼び出しが有効になり、オリジナルのjQueryオブジェクトを返すために支払うことを保証するために、「トランザクション」を提出します。
また、実装するのは非常に簡単です:
- 「ビジネスオブジェクト」を作成し、オブジェクトにjQuery.fn上のすべての機能をコピーします。
- 関数を呼び出すときに、ベースの事前準備された「キュー」で呼び出された関数名とパラメータを追加します。
- トランザクションがコミットされると、選択の要素は、内のすべてのアプリケーション機能横断各ノードごとに一回トラバース「キュー」。
次のように単純にコードは次のとおりです。
var slice = Array.prototype.slice;
jQuery.fn.begin = function() {
var proxy = {
_core: c,
_queue: []
},
key,
func;
//复制jQuery.fn上的函数
for (key in jQuery.fn) {
func = jQuery.fn[key];
if (typeof func == 'function') {
//这里会因为for循环产生key始终是最后一个循环值的问题
//因此必须使用一个闭包保证key的有效性(LIFT效应)
(function(key) {
proxy[key] = function() {
//将函数调用放到队列中
this._queue.push([key, slice.call(arguments, 0)]);
return this;
};
})(key);
}
}
//避免commit函数也被拦截
proxy.commit = jQuery.fn.commit;
return proxy;
};
jQuery.fn.commit = function() {
var core = this._core,
queue = this._queue;
//仅一个each循环
core.each(function() {
var i = 0,
item,
jq = jQuery(this);
//调用所有函数
for (; item = queue[i]; i++) {
jq[item[0]].apply(jq, item[1]);
}
});
return this.c;
};
テスト環境
以下の条件を用いてテスト:
- 容器(<DIV ID = "コンテナ"> </ div>)内にDIV 5000。
- $( '#コンテナを> DIV')使用して、この5000のdivを選択します。
- 各ランダム背景色(randomcolor機能)、及びランダム幅(randomWidth機能)800ピクセル以下に設定する必要がDIV。
3をしている試験方法を取るために呼び出します。
-
通常の使用方法:
$('#container>div') .css('background-color', randomColor) .css('width', randomWidth);
-
シングルラウンドロビン:
$('#container>div').each(function() { $(this).css('background-color', randomColor).css('width', randomWidth); });
-
総務法:
$('#container>div') .begin() .css('background-color', randomColor) .css('width', randomWidth) .commit();
-
オブジェクトの割り当て方法:
$('#container>div').css({ 'background-color': randomColor, 'width': randomWidth });
クロム8(測定直接IEとリンク)を選択するためのテストシリーズブラウザ。
悲しい結果
元の予測結果は、1回のラウンドロビン方式の効率は、通常の使用よりもはるかに高い一方、取引法、単一ラウンドロビンの一部よりも遅いが、それは通常の方法を使用してより速くなければならない、とオブジェクト割り当て方法は、jQueryの支持体の内部に実際にあるもののシングルラウンドロビン、効率は最高でなければなりません。
次のように残念ながら、しかし、結果は以下のとおりです。
通常の使用を法律 | シングルラウンドロビン | 総務法 | オブジェクトの割り当て方法 |
18435ms | 18233ms | 18918ms | 17748ms |
ビューの結果の観点から、取引方法は、メソッドの最も遅いとなっています。同時に、通常の使用で、単一サイクルとは明らかな利点が存在しない、jQueryの割り当て方法の内側にも依存オブジェクトは、実装も大きなギャップを開きました。
作動要素5000は既に非常に大きい循環であるため、このような大規模な循環は、パフォーマンスのギャップを広げることができなかった、通常は最も一般的に明確な利点を有する可能性が低い、約10個の要素を操作する使用、にも拡張不利であり得ますの。
単一サイクル法自体は、パフォーマンスを大幅に向上させるので、単一サイクルに依存し、トランザクション処理を外部の単一サイクルの上に構築されていないため理由は、自然のサイクルが必要な単一に基づいています追加のトランザクションオブジェクトを作成し、機能キュー、キュートラバーサルコスト関数を保存し、その結果を使用する通常の方法でも合理的である失われました。
この時点で、失敗したパスの最適化「業務」を模倣することを発表することができます。しかし、また結果をさらに分析します。
どこでパフォーマンス
まず、試験方法の通常の使用とオブジェクトを割り当てる最速の方法を比較するために、コードアップ分析を使用し、サイクル数(二つの異なる要素間の唯一の違いは、ここでのjQueryの内部問題を取っておくことが言えます実際には)確かに、オブジェクトの割り当て方法をドラッグ後ろ足の疑いがあるが、幸い深刻ではないjQuery.accessを達成悪く、通常の方法を使用すると、10000個の要素である割り当て方法は5000個の要素であるオブジェクト。全体の実装プロセスのおよそ3.5%、全体ではなく、実装プロセスのバックボーンを占めて5000サイクルの時間のかかる要素である、それは本当に最適化のためには必要ではありません17748 =の687ms - だから、単純に、18435を検索します。
そして、それのコストの別の96.5%は消えて?、Doglas文を覚えている「実際にはJavascriptが遅いではない、遅いは、DOM操作です。」実際には、支出の残りの96.5%は、その上の基本的な消費関数呼び出しとの除去は、DOM要素が変更された後の時間の少なくとも95%が、スタイルに再レンダリングに費やされているがあります。
この事実を発見した後、実際には、より正確な最適化の方向性を持つことになります、それはフロントエンドのパフォーマンスの基本原則の一つである:DOMツリーのうち、サブ要素の数が多い、最初のルートの親DOMノードを変更するとき。次のコードを使用するのであれば、再びテストします:
//没有重用$('#container')已经很糟糕了
$('#container').detach().find('div')
.css('background-color', randomColor)
.css('width', randomWidth);
$('#container').appendTo(document.body);
テスト結果は、常に、以前のデータは、上記の一桁、実際の最適化の成功ではなく、周りの900msのままです。
レッスンと概要
- だけ間違った道を導く正しいパフォーマンスのボトルネックを発見して、ブラインド投機と極端を最適化するようにしてください。
- データは、誰もが話していないデータの前に、話します!
- 私は最適化することができ、他のポイントを持っていますが、jQueryのネイティブは、このような「ビジネス」の概念をサポートすることができるようになります場合は、この方向での「トランザクション」は、間違っていると思いませんか?例えば、取引意志のようなDOMツリーのうち、自動的に親要素......
ます。https://www.cnblogs.com/GrayZhang/archive/2011/02/05/a-failure-in-jquery-optimization.htmlで再現