jQuery Performance Tips
     Frontrend Vol.4 powered by CyberAgent
@pocotan001
  Hayato Mizuno
マシンの仕事を


速くするには、少なくしろ
   - 速く動くコードの書き方 -




                     photo by Andrew Morrell Photography
最適化されたコード?

$(document.getElementById('target').

getElementsByTagName('p'))...




          パフォーマンス◎ 可読性△
最適化されたコード?

$('#target p')...




     パフォーマンス△ 可読性◎
利用率の推移

                                     jQuery

                                                                               54.9%
          None
                                   50.8%
 49.1%

                  46.7%


 42.8%

                                                                               39.2%

2012/01          2012/05         2012/09                                     2013/01


                           http://w3techs.com/technologies/history_overview/javascript_library/all
アジェンダ



- ファイルサイズを減らす

- セレクタのチューニング

- リフローの影響を考える
ファイルサイズを減らす
34kb    33kb    33kb
                        32kb
                30kb                                     30kb
        27kb


20kb




1.3.2   1.4.4   1.5.2   1.6.4   1.7.2   1.8.3   1.9.0   2.0.0b1
1kbにつき1msのパース時間
                        (モバイルデバイス)


                                 34kb    33kb    33kb
                         32kb
                30kb                                      30kb
        27kb


20kb




1.3.2   1.4.4   1.5.2    1.6.4   1.7.2   1.8.3   1.9.0   2.0.0b1
IE6-8対応用スニペット

<!--[if lt IE 9]>

    <script src="jquery-1.9.0.js"></script>

<![endif]-->

<!--[if gte IE 9]><!-->

    <script src="jquery-2.0.0.js"></script>

<!--[endif]-->
http://gruntjs.com/
34kb
                                        33kb    33kb
                        32kb
                30kb                                     30kb
        27kb



20kb




                                                 23kb



                                                          20kb
1.3.2   1.4.4   1.5.2   1.6.4   1.7.2   1.8.3   1.9.0   2.0.0b1
jquery / README.md - GitHub
モジュール



- ajax
- css
- effects
- offset
- dimensions
モジュール          $ grunt custom:-ajax,-css




- ajax
- css
- effects
- offset
- dimensions
モジュール          $ grunt custom:-ajax,-css
               Running "custom:-ajax,-
               css" (custom) task
               Creating custom build...
- ajax         Running "build:dist/

- css          jquery.js:*:-ajax:-
               css" (build) task
               Excluding css

- effects       (src/css.js)
               Excluding ajax
               (src/ajax.js)

- offset        Excluding ajax/script
               (src/ajax/script.js)
               Excluding ajax/jsonp
- dimensions   (src/ajax/jsonp.js)
               Excluding ajax/xhr
               (src/ajax/xhr.js)
削除済み/削除予定のAPI



$.uuid, $.attrFn, $.deletedIds,
$.curCSS(), $.sub(),
$.offset.bodyOffset(),
$.fn.andSelf(), $.fn.data('events'),
Deferred.isResolved(),
Deferred.isRejected() ...いっぱい。
削除済み/予定のAPI
<script src="jquery-1.9.0.js"></script>

<script src="jquery-migrate.js"></script>
$.uuid, $.attrFn, $.deletedIds, $.curCSS(),
<script>
$.sub(),($.browser.msie) ...
    if $.offset.bodyOffset(),

</script>
$.fn.andSelf(), $.fn.data('events'),
Deferred.isResolved(),
        Migrateプラグインが便利!
Deferred.isRejected() ...いっぱい。
削除済み/予定のAPI



$.uuid, $.attrFn, $.deletedIds, $.curCSS(),
$.sub(), $.offset.bodyOffset(),
      jQuery.browser is deprecated
$.fn.andSelf(), $.fn.data('events'),
Deferred.isResolved(),
Deferred.isRejected() ...いっぱい。
jquery-migrate / warnings.md - GitHub
セレクタのチューニング
速いセレクタランキング



1. $('#id')
2. $('tag')
3. $('.class')         速い

4. $(':first-child')
5. $(':first')
速いセレクタランキング



1. getElementById
2. getElementsByTagName
3. getElementsByClassName   速い

4. querySelectorAll
5. jQuery Extensions
Example 1



  $('#target p')




       querySelectorAll
Example 1



  $('#target').find('p')




         getElementById
                 +
              .find()
                 +
      getElementsByTagName
Example 1




            +   43%
            (Chrome 24)



                          http://jsperf.com/jquery-child-selectors-ptan/2
Example 2



  $('#target').find('p:first')




               querySelectorAll
                      +
            getElementsByTagName
                      +
               jQuery Extension
Example 2



  $('#target').find('p:first-child')




             querySelectorAll
Example 2



  $('#target').find('p').first()




            getElementsByTagName
                       +
                   .first()
Example 2



  $('#target').find('p').eq(0)




            getElementsByTagName
                       +
                    .eq(0)
Example 2



  $.fn.first = function() {
       return this.eq( 0 );

  };


                  $.fn.first の中身
Example 2




            +   87%
            (Chrome 24)
                          http://jsperf.com/jquery-first-selectors-ptan/2
Example 3



  $('#target').find('p[title="test"]')




               querySelectorAll
Example 3



  $('#target').find('p').filter('[title="test"]')




              getElementsByTagName
                         +
                     .filter()
Example 3




            -   44%
                (Chrome 24)



                              http://jsperf.com/jquery-attribute-selectors-ptan/2
キャッシュの活用も忘れずに

var $target = $('#target'),
      $p = $target.find('p');


$target.on('click', function(e) {

      $p.toggle();
});
リフローの影響を考える
HTML


          DOM


視覚部分を
表すツリー   レンダーツリー   描画


         CSSOM


          CSS
CSS Reflow - jQuery.com
DOMツリー


              html


   head                   body


   title             h1          p


[text node]    [text node]   [text node]
レンダーツリー                       ツリーに挿入されない要素




               root
                                 display:none


   head                    body


    title             h1              p


 [text line]    [text line]      [text line]
レンダーツリー                       ツリーに挿入されない要素




               root
                                display:block


   head                    body


    title             h1             p


 [text line]    [text line]      [text line]

      リフロー!レイアウトの計算を行う処理のこと
HTML


  DOM


レンダーツリー   描画


 CSSOM


  CSS
HTML


       DOM     リペイント!


JS   レンダーツリー   再描画


      CSSOM


       CSS
HTML
$('p').css('margin', '5px')

              DOM


 JS        レンダーツリー            再描画


             CSSOM             +
                              リフロー

               CSS
HTML
$('p').css('color', 'red')

              DOM


 JS        レンダーツリー           再描画


             CSSOM


               CSS
リフロー 2?

$('p').css('margin', '5px')




$('p').css('margin', '5px')
1 位置変更がないため
リフロー 4?

$('p').css('margin', '5px')
      .css('padding', '5px')
      .css('top', '5px')
      .css('left', '5px');
1 可能な限り収束される
収束が難しいケース

$('p').css('margin', '5px')
      .css('padding', '5px')
      .css('top', $target.height())
      .css('left', '5px');
2 ...あれ?
最近のChrome賢すぎ...
                  photo by pedrosimoes7
2 Safariでやります
    (スミマセン...)
リフロー?リペイントのみ?


- display
- visibility
- opacity
- border
- border-radius
リフロー?リペイントのみ?


- display
- visibility      リペイントのみ

- opacity         リペイントのみ

- border
- border-radius   リペイントのみ
リペイントのみでもモノによっては高コスト


-   color: rgba()
-   opacity
-   background: linear-gradient()
-   border-radius
- text-shadow
- ... etc
                                    See also
リフローが発生する可能性のあるトリガー


- CSSの変更/取得
 css(), addClass(), show(), animate() ...

- DOM要素の操作
 html(), text(), append(), focus() ...

- 特定のプロパティの取得
 offset(), position() ...

- ユーザー操作
 ウィンドウサイズの変更, スクロール, テキストの入力 ...
                                            See also
不要なリフローを避ける
Example 1



  $('<p>test</p>').appendTo('body').hide();




                 生成時に色々と追加が
                   必要なケース
Example 1



  $('<p>test</p>').hide().appendTo('body');




                  描画する前に行う
Example 1




            Before
Example 1




            After
Example 2



  $('<img src="200x100.jpg">').
      appendTo('body');




                  imgを生成するケース
Example 2



  $('<img src="200x100.jpg" width="200"
  height="150">').

      appendTo('body');


               描画の領域を明示しておく
                 CSSで指定しても×
Example 2




            Before
Example 2




            After
Example 3



  $('p').css('top', $target.offset().top)
        .css('left', $target.offset().left);




            複数回に分けて実行されるcss()
Example 3



  $('p').css({
        top: $target.offset().top,

        left: $target.offset().left
  });



              1回のcss()にまとめる
Example 3



  $('p').css({
        top: $target.offset().top,

        left: $target.offset().left
  });


            リフローが必要な取得系メソッド
Example 3



  var offset = $target.offset();


  $('p').css({
        top: offset.top,

        left: offset.left
  });
                 可能ならキャッシュして使い回す
Example 3




            Before
Example 3




            After
Example 4



  <script src="jquery.js"></script>

  <script src="jquery-ui.js"></script>

  <script>

  $(function(){ $('#target').accordion(...); });

  </script>

  </body>
              UI表示後にスタイルを変更するケース
Example 4




    表示             JS          変更




            読み込み, パース, 実行...
             この間は待ち時間
Example 4



  <script src="jquery.js"></script>

  <script src="jquery-ui.js"></script>

  <script>

  $(function(){ $('#target').accordion(...); });

  </script>

  </head>
               <head> に移す
Example 4




    JS      表示&更新
Example 4



その他の代替案
- スタイル付けはCSSでやる

- async属性で非同期にする (IE10∼)

- 既存の非同期ローダーに乗っかる (RequireJSとか)
ボトルネックを解消する
ボトルネックになりそうなところ


- 描画コストの高いCSSプロパティ

- アニメーション

- ループ処理

- scroll, resize などの発火回数の多いイベント

- ... etc
Google Chrome Canary
jQuery Animations < CSS3




    http://dev.opera.com/articles/view/css3-vs-jquery-animations/
requestAnimationFrame




  https://github.com/gnarf37/jquery-requestAnimationFrame
throttle / debounce




http://ktkne.st/elab/post/2012/strcount-throttle-debounce.html
その他



- スタイルの変更は出来るだけ末端要素で行う


- アニメーションは固定配置にする


- テーブルレイアウトをしない
戒めのお言葉
小さな効率は忘れよう。
時間の97%について語ろう。
早まった最適化は諸悪の根源だ。
              Tony Hoare?




                            photo by glingl
Thank you




            photo by Furryscaly

jQuery Performance Tips – jQueryにおける高速化 -