Posted at

jQueryのパフォーマンスを下げるアンチパターンに関する超意訳

More than 5 years have passed since last update.

以下のスライドを意訳したものです。Compress周りについては触れていません。「いやいや、最新の書き方だともっと良い書き方があるんだよ!」という方のコメントをお待ちしております!

http://www.slideshare.net/paul.irish/perfcompression


クエリをキャッシュする

// 悪い例

var id = $("#content").data("id");
var itemId = $("#content").data("item-id");

// 良い例
var content = $("#content")
var id = content.data("id");
var itemId = content.data("item-id");


HTMLを生成するときにjQueryオブジェクトにアクセスし続けない

// 悪い例

$.each(reallyLongArray, function(count, item) {
var newLi = '<li>' + item + '</li>';
$('#ballers').append(newLi);
});

// 良い例 : DocumentFragmentを使用
var frag = document.createDocumentFragment();
$.each(reallyLongArray, function(count, item) {
var newLi = '<li>' + item + '</li>';
frag.appendChild(newLi[0]);
});
$('#ballers')[0].appendChild(frag);

// 良い例 : Stringを使用
var myhtml = "";
$.each(reallyLongArray, function(count, item) {
myhtml += '<li>' + item + '</li>';
});
$('#ballers').html(myhtml);


Keep things DRY

// 悪い例

if ($ventfade.data('currently') != 'showing') {
$ventfade.stop();
}
if ($venthover.data('currently') != 'showing') {
$venthover.stop();
}
if ($spans.data('currently') != 'showing') {
$spans.stop();
}

// 良い例
var elems = [$ventfade, $venthover, $spans];
$.each(elems, function(k, v){
if (v.data('currently') != 'showing') {
v.stop();
}
});


アンチパターン:オブジェクトリテラルを使わない宣言

無名関数で定義されているせいで、どこで何が起こっているのか判断がつかなくなってしまうケース。

$(document).ready(function() {

...
$('#magic').click(function(e) {
$('#yayeffects').slideUp(function() {
...
});
});
$('#happiness').load(url + ' #unicorns', function() {
...
});
});

オブジェクトリテラルを使用して記述するようにする。

var PI = {

onReady: function() {
...
$('#magic').click(PI.candyMtn);
$('#happiness').load(url + ' #unicorns', PI.unicornCb);
},
candyMtn: function(e) {
$('#yayeffects').slideUp(PI.slideCb);
},
slideCb: function() {
...
},
unicornCb: function() {
...
}
};

オブジェクトリテラルを使用することで、下記の利点を得られる。


  • コードの見通しがよくなる。

  • プロファイラで関数名を参照できる。

  • 各関数をコンソールで実行してみることができる。

  • 無名関数のときと比べて、テストが書きやすくなる。


アンチパターン:再クエリ

// create and append your element

$(document.body).append('<div class="baaron" />');
// requery to bind stuff
$("div.baaron").click(function(){ });

// better
// swap to appendTo to hold your elem
$('<div class="baaron" />')
.appendTo(document.body)
.click(function(){ });


$('#whats .the', context)

// パフォーマンスはこっちよりも

var arms = $('div.robotarm', '#container');
// こっちの方がいい
var arms = $('#container').find('div.robotarm');

// 読みやすさ的にはcontext?好みの問題かも。。


セレクタのパフォーマンス最適化

一番パフォーマンスが良いのは#idから下降する書き方。

// 普通の書き方

var arms = $('#container div.robotarm');
// もっと良い書き方
var arms = $('#container').find('div.robotarm');

不必要なセレクタを挟まないほうが、より高速にヒットする。

// 余計なものが混じっている

.data table.attendees td.gonzalez
// 中間をはぶこう
.data td.gonzalez

ユニバーサルセレクタはパフォーマンスを劣化させる。明示的に書いていなくても、暗黙のユニバーサルセレクタが使われている場合があるので、注意すること。

$('.buttons > *') // 超コストがかかる

$('.buttons').children() // 良い例

$('.gender :radio') // 暗黙のユニバーサルセレクタが含まれている
$('.gender *:radio') // 上記と同じ意味になる
$('.gender input:radio') // 良い例

効率的なCSSの書き方(https://developer.mozilla.org/ja/Writing_Efficient_CSS)


イベントのデリゲート

live()を使うより、delegate()を使うことでより高速化できる。

// 汚い書き方。。

$('a.trigger', $('#container')[0]).live('click', handerFn);

// 良い書き方
$('#container').delegate('click', 'a.trigger', handlerFn);


DOMの操作は総じて重い

タグのスタイル属性を直接操作するのはもってのほか。スタイルをいじりたい場合はクラスを変えよう。(DOMの操作は最小限におさえるのが原則)


ブラックボックスとしてjQueryを扱わない

単にjQueryを隠蔽する書き方は、単に可読性を下げるだけだ。

// これでは単にjQueryを隠蔽しているだけで、無駄にメソッドを増やしていだけに過ぎない。。

getScript: function( url, callback ) {
return jQuery.get( url, null, callback, "script" );
},
getJSON: function( url, data, callback ) {
return jQuery.get( url, data, callback, "json");
},

よく使うメソッドは覚えておこう。

map(), slice(), stop(), (de)queue(), prevAll(), pushStack(), inArray(), etc…

// index() in jQuery <= 1.3.2

$('#rdworth').parent().children().index( $('#rdworth')[0] )

// prevAll()を使うと10%速くなる
$('#rdworth').prevAll().length

// jQuery1.4からはこう書ける
$('#rdworth').index()


存在しない要素に対するアクションを書かないこと

jQueryは凄く空気を読んでくれるので、そんなコードを書いたとしてもエラーは出さない・・・けど、ものすごいオーバーヘッドが裏で生じることになる。

$('#doesntexist').slideUp()

// genFx(), speed(), animate()といったメソッドが実行されるが、要素は存在しないため画面上では何の動きもあらわれない


便利なメソッド


eq(), first(), last()

var lastelem = $elems.eq(-1); // get()と同じ

$('#nav li:first') === $('#nav li').first();
$('#nav li:last') === $('#nav li').last();


data()

// 普通の書き方

$(elem).data(key, value);

// 10倍速い!
$.data(elem, key, value);