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

  • 280
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

以下のスライドを意訳したものです。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);