Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

jQuery Proven Performance Tips And Tricks (翻訳)

http://addyosmani.com/jqprovenperformance/
このサイトに書いてある内容をまとめます。英語は苦手なんで適当な意訳が多いです。

Performancetip1 バージョンのお話

  • 可能な限り常に最新バージョンを利用する。
  • アップデートする前にはかならずregression testを行う事。
  • 最新のバージョンは1.6.2や1.7で、この秋にリリースされる予定。
  • twitterなどもjquery1.3.0を利用している。
  • 1.6はパフォーマンス改善や 新しい特徴が組み込まれている。
  • 1.6.2はバグのパッチやregressionが修正される。
  • 古いバージョンではこれらお手軽なパフォーマンスを受けることができない。
  • 47%の有名なサイトがJqueryを使っているが、バージョン変更にテストが大変。
  • アップデートは通常、大変ではないプロセスである。
  • 1.4.2、1.4.4と1.6.2のSelectorを比較してもパフォーマンスの違いは明らか。
  • .attr( 'value' ), .val()(get)なども1.6でパフォーマンスが改善されている。
  • しかし特定のselectorは1.4.xより遅くなっている。
  • 使っているselectorのパフォーマンスに注意すれば、大丈夫。

Performancetip2 Selector

  • 全てのSelectorは平等に作られない。
  • Selectorの生成方法は沢山あるし、それぞれのSelectorは同一のパフォーマンスを出さない。
  • 最速と最遅いSelectorがどれか知っていますか?
  • IDとElementのSelectorは最速。$( '#Element, form, input' )
    • IDとElementでの指定は裏側でnativeDOMの操作を利用している。(getElementById())
  • Class Selectorは遅い。#( '.element' )
    • getElementsByClassNameはIE5-8ではサポートしていない
    • Firefox3、Safari4、Chrome4、Opera10.10以上はこれは速い。
  • 最も遅いのは正しくない指定とattributeでのselector。$( ':visible, :hidden' ); $( '[attribute=value]' );
    • 上のselectorはnative関数を呼び出す事ができない。
    • 最新のブラウザーにはquerySelector()やquerySelectorAll()などサポートされている。
  • querySelectorAll()はCSSベースでのSelectorとなる要素をDOMとして検索できる。
    • JQueryはquerySelectorAll()の利用を試みている。
    • querySelectorAllを使うSelectorの最適化と:first,:last,:eq等ではないSelectorの最適化との比較。
    • 有効なSelectorこれをより利用する機会がある。
  • 曖昧なselectorは有効ではあるが、使う時には注意が必要。
  • :hiddenを指定した要素が100個あったとき、JQueryは100個のアイテムをコールする。
  • :hiddenは有効であるが全ての曖昧な指定は全ての要素に対して実行を試みる。
  • できれば上の事は避けたい。
  • jQuery1.4.2と1.6のselectorについての比較は http://jsperf.com/dh-jquery-1-4-vs-1-6/6 に記載されている
  • jQuery1.2.xと1.4.xと1.6.xとquerySelectorAllとquerySelector()と他のフレームワークについては http://jsperf.com/jquery-vs-sizzle-vs-midori-vs-mootools-selectors-test/26に記載されている。

Performancetip3 parentとchildrenの理解

  • parentとchildrenの理解が必要。1.context,2.find,3.immediate children, 4.child combinator selector, 5.class selector, 6.created context。
  • 1. context: $( '.child', $parent ).show();
    • 上の文法を$.parent.find( 'child' ).show();と変更してしまうと遅くなってしまう。
    • 5-10%最速のオプションと比較すると遅くなる。
  • 2. .find(): $parent.find( '.child' ).slow();
    • これは最速。
  • 3. immediate children: $parent.children( '.child' ).slow();
    • この文法は内部的に$.siblingとネイティブ関数のnextSibling()を同じ木構造の他の要素を参照してしまう。
    • これは最速のオプションより最大で50%ほど遅くなってしまう。
  • 4. css child combinator selector : $( '#parent > .child' ).slow();
    • child combinator selectorを使う事は、右から左の要素に働きをかけてしまう。
    • childのチェックをする前にparentの直接のchildとしてmatchしてしまうので、良くない。
    • これは最速のオプションより最大で70%ほど遅くなってしまう。
  • 5. css class selector : $( '#parent .child' ).show();
    • class selectorを使う事は4と同じ制約を受ける。
    • これは内部的には.findを使うように変換される。
    • これは最速のオプションより最大で77%ほど遅くなってしまう。
  • 6. created context : $( '.child', $( '#parent' ) ).show();
    • これは内部的に$( '#parent' ).find( '.child' )を実行しているのとほぼ同じ。
    • これは最速のオプションより最大で23%ほど遅くなってしまう。
  • まとめると、最速のオプションは2の$parent.find( '.child' ).show();
    • parent selectorは既にキャッシュされるのでDOMから再取得する必要はない。
    • キャッシュが無いと16%ほど遅くなる。
    • ネイティブのgetElementByIdやgetElemetsByName,getElmentsByTagNameを直接使用する事は渡されたcontextを検索することである。
  • .findはtop-downでの全ての子要素を検索する時にパフォーマンスを発揮する。
  • やりたい事によって他のオプションはより最適だったり、パフォーマンスを発揮するかもしれない。
  • contextとselector,selectorと.find()とparent/child selectorとimmediate chidrenに関する記述は http://jsperf.com/jquery-selectors-context/2 に記載されている。

Performancetip4 絶対に理解すべき項目を抑えて、JQueryを使うべき。

  • 通常のJavascriptを使った方がよりパフォーマンスを出す時があることを覚えておくべき。
  • jQueryJavascriptである。
  • このことに使う人は何度遭遇するか。
  • jQueryの過剰な利用。
    • $( 'a' ).bind( 'click'. function() { console.log( 'You clicked:' + $(this).attr( 'id' ) ); });
    • ID selectorは生成されたjQueryオブジェクトと構文解析された後のselectorのdocument.getElementByIdを取得するだけである。
  • なぜ自身のDOM要素を利用しないのか。これはより速い。
    • $( 'a' ).bind( 'click'. function() { console.log( 'You clicked:' + thid.id ); });
    • overheadを解消するこのjQueryは常にベストな方法ではない。
  • this.idと$(this).attr('id')の両方ともに同じ値を返すが、以下の事を覚えておくべき。
    • 低いレベルではthis.getAttribute('id')は$(this).attr('id')と等価である。
    • しかしながらattributeを最新に保つためにthis.idを使う方がよりよい。
  • $(this).attr('id') とthis.idの比較は http://jsperf.com/el-attr-id-vs-el-id/2に記載されている。
  • $(thid).attr('id')は直接DOMの要素を参照するよりも80-95%ほど遅くなる。

Performancetip5 cachingは大変役立つ

  • cachingは後でselectorの結果を再利用することを意味する。
  • 以下のことを覚えておくべき。
    • それぞれの$( '.elem' )はDOMを検索した結果を再実行し、新しい結果を取得する。
    • cacheされた結果を取得すれば何でも実行できる。
    • cachingは選択の繰り返しを減らす。
  • cacheされた結果を使う事
    • var foo = $( '.item' ).bind( 'click', function({ foo.not(this).addClass( 'bar' ).removeClass( 'foobar' ).fadeOut(500); }) );
  • cached selectorと要素参照の繰り返しについての比較は http://jsperf.com/ns-jq-cached ここに記載されている。
  • cacheされていないselectorはcachedされたものを使うよりどの場合でも62%遅くなってしまう。

Performancetip6 chaining

  • 全てのjQuery methodはjQuery objectを返却しchainingをサポートしている。
  • これはselection上でmethodを続けて実行できることを意味する。
  • より少ないコードでより記述が簡単である。
  • No-chainigとchainigの比較。
  • chainedされた呼び出し方、それぞれの呼び出し方、cachedされたそれぞれの呼び出し方の比較は http://jsperf.com/jquery-chaining ここに記載されている。
  • chainingが最速で、それに次いでcacheされた個別の呼び出し方となる。

Performancetip7 event delegation

  • event delegationはDOM構造に対してeventを作成し、追加することを可能にする。
  • これにおいて重要なことはsingle event handlerを結びつける事である。
  • 実行時にDOMの中でelementsに対して動作する。またこれらは実行後に導入されるとも言える。
  • .bind()
    • .bindは要素に対してclickやmouseenterなどのイベント処理を結びつける。
    • 大量の設定を伴う場合、browserは全てのイベント処理を追跡しなければならないし、それはbindするために時間がかかってしまうことがある。
    • 要素に動的に設定する事は働かない。
  • .live()
    • event delegationのサポートする最もシンプルな形式。
    • これはselectorの今と後にマッチすることをイベント処理に対して加える。
    • 単純なシナリオについては最適に働くが、欠陥もある。( 最上位層の結合でなければならなく、横断すると失敗する)
    • 他のjQueryのmethodと違ってchainすることができない。
  • .delegate()
    • .delegateは今と将来に一致するselectionに対してハンドラを追加するときにbindするような特別なDOM要素に対して指定する事を認める。
    • 確実にelementのターゲットをキャプチャするために全ての方法でDOMに発散できない。(.live()と違って)
    • 同じイベント処理を他の多数の要素に追加する時に使うといい。
  • .live()や.delegate()やdelegate from body variationsについては http://jsperf.com/jquery-delegate-vs-live-table-test/2 に記述されている
  • .bind()や.click()や.live()や.delegate()については http://jsperf.com/bind-vs-click/12 に記述されている。
  • .live()や.live() contextや.delegate()やdelegating to document.bodyについては http://jsperf.com/jquery-live-vs-jquery-delegate/15 に記述されている。

Performancetip8 DOMはDatabaseではない

  • jQueryはDOMをDatabaseのように扱う事ができるが、Databaseではない。
  • それぞれのDOMに対して要素を追加する事はコストが掛かる事を覚えておく。
  • これは.append()や.insertBefore()や.insertAfter()の使用を最小限に保つことを意味する。
  • contentや.text()や.html()に格納されている情報の取得をする横断的なDOMはたいてい最適なアプローチではない。
  • .data()を代わりに使う事はDOM要素に対して様々な要素を安全に追加することを許可する。
  • Tip1 .append()の使い方。
    • メモリの中にHTML文字列を構築する事によって最小化を図り、単体の.append()を代わりに使う。
    • cacheされたselectorを追加する時ではなく、それらより20%遅くなる時に多数のappend()は90%遅くする。
  • Tip2 .detach()の使い方
    • nodeに対して重い追加を行う時に大変よく動作する。
    • 既に一度準備されている時にはDOMへのnode再追加を許可する。
    • undetachedのnodeよりも60%早く動作する。
  • Tip3 .data()のよりよい使い方
    • 通常のdataは以下のように使う。$( '#elem' ).data( key, value );
    • しかい実際は以下の方法がより速い。$.data( '#elem', key, value );
    • これはjQueryオブジェクトの生成にoverheadがある事と、最初にdataのパースを行うからである。
  • $.dataはより速いけれども、selectorに対して適用はできず、nodeに対してだけである。
  • これは$.data( elem, key,value )はelemが既にelementとして定義されている場合にのみ利用可能であることを意味する。
  • .detachとdetachingでは無い比較は http://jsperf.com/to-detach-or-not-to-detach ここに記載されている。
  • jQuery.dataとjQuery.fn.dataの比較は http://jsperf.com/jquery-data-vs-jqueryselection-data/11 ここに記載されている。
  • 多数のappendと単一のappendの違いについては http://jsperf.com/string-concat-single-append-vs-multiple-append ここに記載されている。

Performancetip9 LOOPを理解する

  • $.eachや$.fn.each()を使うよりもnativeのforやwhile loopを使う方がより速いことを知っていたか?
  • jQueryは指定されたcollectionsのiterateを簡単にするが、それらはいつも最適に働くとは限らない。
  • Ben Almanの$.each2()ようなPluginは時々$.fn.eachよりも良く動作する。
  • 出来る限りのLoopは避けるべきだが、難しいのはネストされたDOMのselectorsはよりよく動作する。
    • 必ず必要とは言い切れないが、Loopは避ける。これは全てのプログラミング言語を遅くする。
    • 可能であれば、selector engineを使う代わりに必要なelementsにアクセスする。
    • 当然ながら置換不可能なloopも存在するが、最適化のために最善の努力をすべき。
  • 何がいいたいかと言うと
    • 開発者はよくiterateを必要とする。
    • $.eachによって提供されるクロージャの範囲は通常他の理由により求められる。
    • Loopは使わなければ行けないところでのみにすべきで、しかしそれらは置き換え可能である事を覚えておくべき。
  • jQuery.eachとfor,while,reverse for, jQuery.fn.eachや他のloopのアプローチは http://jsperf.com/jquery-each-vs-for-loop/24 ここに記載されている。
  • jQuery.fn.eachとBen Alman'sの.each2()は http://jsperf.com/jquery-each-vs-quickeach/3 こちらに記載されている。

Performancetip10 必要の無いjQuery Objectを新たに生成することは避ける

  • $( 'a' ).map( function() { return $( this ).text(); } );
  • 開発者は共通で上のようななにかのtextにアクセスするような場合、jQuery Objectを生成してしまう。
  • $.fn.method()よりも$.method()を使う方がパフォーマンスを改善する助けとなる。
  • 全てのjQuery methodがそれぞれの単体node関数を所有しているわけではない。
  • JamesはjQuery.single()という解決方法を提案した。
  • jQuery.single()を全てのjQuery objectの呼び出しに利用したり、単体のDOM要素に対してのみ適用できる。

Bonus Tip コードのDRYは保つべき

  • 同じコードを繰り返す事はコードのサイズを増加するだけでなく、製品としての質を下げる事になる。
  • DRY( don't repeat yourself )はそれぞれの知識に対する一つの表現を推奨する。
  • コードを最小限に保つ事はchainigやcachingやその他のことで手助けする事を思い出させてくれる。