Javascriputを簡単に書けるようにしてくれるライブラリのjQueryですが、使い方をちょっと変えるだけで、パフォーマンスに大きく差がでます。ハイスペックPCでは分かりづらいかもしれませんが、スペックの低いPCやモバイルではWebページの表示速度が遅くすぐに閉じられるかもしれません。
Webページがたった1秒高速化するだけでECサイトなんか、売上が10%向上した事例もあります。そこで、jQuery高速化のポイントや方法をここにまとめておきます。
jQueryの最新バージョンを使う
- https://jsperf.com/speed-comparison-of-jquery-versions
jQueryは常にバージョンアップし続けています。画像1は、jQuery「1.6」「1.7」「1.8」「1.9」「2.0 beta」バージョンの速度を比較を行ったものです。数値が高いものほど速度が高いことを示しています。betaを除いて、「1.9」がもっとも速いことから、最新のバージョンはより軽量で高速に処理してくれていることが伺えます。
「1.x」と「2.x」バージョンを使い分ける
jQueryにはIE6/7/8をサポートしている「1.x」バージョンとIE6/7/8のサポートを廃止した「2.x」バージョンがあります。「2.x」では、IE6/7/8に必要だった細かなハックを取り除き、モダンブラウザにフォーカスしたことによってより軽量に速く安定した動作を目指したものです。
「1.x」と「2.x」バージョンをのファイルサイズを比較したところ10%ほど小さくなっていました。
なので、IE6/7/8には、「1.x」バージョンを読み込ませ対応しつつ、モダンブラウザには「2.x」バージョンを適用すれば速く安定した動作がえられます。
サンプルコード
<!–[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]–>
CDNサービスからライブラリを読み込む
自サーバーでjQueryを読み込んで使うより、CDNサービスを利用した方がWebサイトを高速化できる可能性があります。
CDNって何?
CDNとは「Contents Delivery Network(コンテンツデリバリネットワーク)」または「Contents Distribution Network(コンテンツディストリビューションネットワーク)」のことで、コンテンツを配信するために最適化されたネットワークをを指します。
例えば、GoogleのCDNサービス「Google Libraries API」があります。Google自前のサーバにjQueryライブラリがおいてあってそれを読み込んで使えるといった感じです。
CDNでjQueryを使用するメリット&デメリット
メリット
- 高性能なサーバ側での圧縮転送(gzip)機能に対応し、高速化が期待できる
- 同じURLからファイルを取得していれば、ユーザーのキャッシュ効果が期待できる
- 自サーバにjQueryを置く必要がない※フォールバックを行う場合はその限りではない
デメリット
- インターネットに繋がっていないローカル環境では使えない
- CDNがなんらかのトラブルが起こった時使えない
まず、ローカル環境では、ダウンロードして使えば問題ないでしょう。2つ目の何らかのトラブルが起きた場合は、フォールバックという「代替手段」を使って回避出来ます。これについては後述します。
使用方法
主な無料で利用できるCDNは、Google、Microsoft、jQuery、CDNJS、の4つがあります。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
Googleではライブラリ表記を以下のようにも指定出来ます。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10/jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
「1.10.1」のマイナーバージョンまで指定するすると、キャッシュは1年後に、Expireする指定になっています。「1.10」のように指定したすると、1.10の最新バージョンが自動的に読み込まれます。「1」のように指定すると、最新バージョンが読み込まれます。ただし、キャッシュは24時間が有効期限となります。
すでにGoogleCDNからjQueryをダウンロードされていてキャッシュが残っていれば、ブラウザが再びダウンロードする必要がなくなります。そのためブラウザがjQueryをダウンロードする機会が圧倒的に少なくなるためjQueryの動作が高速化します。ですので、1年キャッシュを保持してくれるマイナーバージョンまで指定するべきでしょう。
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.2.min.js"></script>
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
CDNから読み込めない場合を考慮(フォールバック)
CDNサービスが故障したり、なんらかのトラブルで使えなくなった時、Webサイトも共倒れにならないように代替手段を打っておくことを推奨します。
次の2行目の指定を行うことで、1行目でCDNサービスからコンテンツを読み込めなかった場合でも、自サーバのコンテンツを読み込むことができます。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script> <script> window.jQuery || document.write(‘<script src="jquery-1.10.1.min.js"></script>’) </script>
セレクタの指定はIDを使う
jQueryでは指定したセレクタにマッチする要素を探すのに、負荷が掛かり、要素数が多いほど大きくなります。負荷が大きくなると処理速度も遅くなり、アニメーションもなめらかなでなくなるので、できる限り負荷がかからないようにしましょう。jQueryで、負荷をかけない最速で要素の選択をするセレクタは、classよりIDを使うことです。
classとIDの速度比較
classとIDでどれくらい速度に差があるのか、比較検証をしてみます。
検証環境
- ブラウザ:Chromeバージョン27.0
- jQueryバージョン:1.10.1
指定範囲の処理時間を表示できるconsole.time()
を使って比較してみました。単位はミリ秒です。以下のような検証用コードで行いました。
HTML
<div id="list"></div>
jQuery(ID処理速度測定)
$(function() { // IDの検証 console.time(‘id’); var list = $(‘#list’); var items = ‘<ul>’; for (i=0; i<1000; i++) { items += ‘<li id="item’ + i + ‘">item</li>’; } items += ‘</ul>’; list.html(items); for (i=0; i<1000; i++) { var s = $(‘#item’ + i); } console.timeEnd(‘id’); // IDここまで });
jQuery(class処理速度測定)
$(function() { // classの検証 console.time(‘class’); var list = $(‘#list’); var items = ‘<ul>’; for (i=0; i<1000; i++) { items += ‘<li class="item’ + i + ‘">item</li>’; } items += ‘</ul>’; list.html(items); for (i=0; i<1000; i++) { var s = $(‘.item’ + i); } console.timeEnd(‘class’); // classここまで });
id="list"
をもつDIV要素の中にliを1000もつul要素を作ります。li要素には、それぞれ個々のclassとIDをもつようにして、それに対して、セレクタを選択し、それに掛かる時間をChromeのDeveloperToolsを使用して計測しました。計測は10回行い、その平均結果を示します。
検証結果
セレクタ | 結果(平均値) | |
---|---|---|
ID | $('#item[x]'); |
25.1ms |
クラス | $('.item[x]'); |
83.3ms |
検証結果からIDで指定した方が、classで指定するよりも3.3倍速いことがわかりました。
結論
セレクタはIDで指定する。
classを指定する時に、classの前にタグ、IDを指定すると処理が速くなるというのを試してみたところ予想に反し、平均116.8msと遅くなりました。
セレクタはシンプルに書く
jQueryは、とても律儀で、セレクタを解析したら、その順番通りに処理を進めるてくれます。親切のつもりで、$('#item');
を$('div#list ul li#item');
と指定すると、まずDIVを探し ID → ul → li → ID と探すので結果余計な処理を行ない、遅くなってしまいます。
検証用コードで比較
IDの検証用コード15行目var s = $('#item' + i);
をvar s = $('div#list ul li#item' + i);
に変えて、検証。
セレクタ | 結果(平均値) | |
---|---|---|
タグ+ID タグ タグ+ID | $('div#list ul li#item[x]'); |
107.6ms |
セレクタでIDを指定した場合平均25.1msでしたので、結果から考えるとシンプルにIDで指定した方が、4.3倍速いことになります。
結論
セレクタを指定する時はターゲットまで最小限の指定をしましょう。
子孫セレクタを指定する
複数の要素を選択する時は、IDが使えません。例えば、下記Formを作成するコードがあるとします。
<div id="entry" class="form-wrap"> <form id="entry-form" class="entry-form" action="#" name="entry-form"> <p><input type="text" name="user-name"></p> <p><input type="text" name="mailaddress"></p> <p><input class="buttom" type="submit" value="送信"></p> </form> </div><!– /entry –> <div id="search" class="form-wrap"> <form id="search-form" class="entry-form" action="#" name="search-form"> <p><input type="text" name="search-user-name"></p> <p><input type="text" name="search-mailaddress"></p> <p><input class="buttom" type="submit" value="検索"></p> </form> </div><!– /search –>
jQueryを高速化する!いろいろなセレクタの書き方を検証してみた|アルパカの具
id="entry-form"
階層の下にあるinput
を選択する場合、よくfind()
を使うと速度があがるいうのをみますが、調べてみたところ以下のような結果が得られました。
検証結果
10000回ループさせて、セレクタを選択した時間を10回行っった平均値を結果に示しています。
セレクタ | 結果(平均値) | |
---|---|---|
ID名 タグ | $('#entry-form input'); |
75.1ms |
ID名 input要素 | $('#entry-form :input'); |
2497.4ms |
IDからfind() | $('#entry-form').find('input'); |
194.7ms |
ID名 > タグ > タグ | $('#entry-form > p > input'); |
84ms |
Context | $('input', '#entry-form'); |
205.4ms |
上記の結果から考えるとfind()
を使うより、CSSチックに書いたほうがいいように感じられます。検証方法などが不適切だったのか疑問も残りますが、:input
要素を指定して選択するよりも、33.3倍速い結果が得られました。
結論
階層下の指定は子孫セレクタ
同じセレクタは変数化する
同じ要素に複数の処理を行いたい時、例えば、formで入力蘭にフォーカスされた時、分かりやすくするために、CSSで表示を変更したり、クラスを加えたり、アニメーションをつけたりしたい時があります。その都度同じセレクタを指定すると、jQueryは、毎回そ処理を行います。その分遅くなるので、一度指定したセレクタを変数に入れて再利用しましょう。
同じセレクタ型
$(‘form[name="entry-form"]‘).addClass( ‘test’ ); $(‘form[name="entry-form"]‘).css( ‘borderColor’,'red’ ); $(‘form[name="entry-form"]‘).animate({‘width’ :’200px’},500);
変数に代入型
var entryForm = $('form[name="entry-form"]'); entryForm.addClass( 'test' ); entryForm.css( 'borderColor','red' ); entryForm.animate({'width' :'200px'},500);
検証結果
10000回ループさせて、処理を行った結果10回分の平均値を示しています。
結果(平均値) | |
---|---|
同じセレクタ型 | 845.8ms |
変数に代入型 | 539.5ms |
上記結果から変数に入れて扱った方が、同じセレクタを複数使うより1.6倍速いことがわかります。
結論
同じセレクタは変数に入れる
連続処理はメソッドチェーンを使う
「同じセレクタは変数化する」で扱った処理はメソッドチェーンを使って処理を実行をすることもできます。
メソッドチェーン
$('form[name="entry-form"]').addClass( 'test' ).css( 'borderColor','red' ).animate({'width' :'200px'},500);
検証結果
結果(平均値) | |
---|---|
メソッドチェーン | 643.7ms |
メッソドチェーンが使える時は、コードが短く速度も早くなるので使った方がいいでしょう。
結論
連続処理はメソッドチェーンを使う
特殊なセレクタを排除する
特殊なセレクタは使わずに、専用のメソッドに置換して使った方が高速化します。
例
// 遅い例 $('#entry-form p:first'); $('#entry-form p:last'); $('#entry-form p:eq(i)'); // ↓↓↓ 置換 ↓↓↓ // 速い例 $('#entry-form p').first(); $('#entry-form p').last(); $('#entry-form p').eq(i);
検証結果
:first
の場合平均2882.7msで、first()
にしたら平均572.7msという結果がでました。この数値から考えると5倍速くなります。
結論
特殊なセレクタはそれに、適したメソッドに置換する
each()よりforを使う
繰り返しの処理をするときはeachよりforを使いましょう。jQueryは内部的にJavaScriptが動いているので、JavaScriptネイティブの命令の方が速くなります。
検証結果
検証コード
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script src="//documentcloud.github.com/underscore/underscore-min.js"></script> <script> var pi = Math.PI var a = new Array(1000000), e; </script>
Underscore.each vs jQuery.each vs. for loop vs. forEach · jsPerf
結果から読み取るとjQuery.eachが一番遅いようです。またfor文でも減少(decrementing)させてループを回した方が少し速いですね。
結論
繰り返し処理は、forを使う
concatとjoin()
文字列を連結する時は、concat
より配列に格納し、join()
で連結した方が速くなるというのをよくみますが、以下のような方法では、concat
の方が速いという結果になりました。
検証コード
<script> var str = 'string'; var barr = ['this', ' is', ' a', str, ' to', ' test', ' if', ' there', ' is', ' any', ' difference', ' between', ' concat', ' and', ' join']; var sarr = ['this', ' is', ' a', str]; </script>
- join/concat · jsPerf
live()、delegate()よりon()を使う
バージョン「1.7」からlive()
、delegate()
は、on()に統一されています。
// live()、delegate() $('a.trigger', $('#container')[0]).live('click', handerFn); $('#container').delegate('click', 'a.trigger', handlerFn); // on() $(document).on('.hoge','event',callback) // live()として $('#fuga').on('.hoge','event',callback) // delegate()として
DOMの操作は最小限に抑える
DOMの操作は重いので、タグのスタイルを変更したい場合は、直接操作するのではなく、スタイルシートにスタイルを記述して、クラスを変えるようにしましょう。
for内にlengthを使わない
forないで.length
を使わないで、変数に入れておく方が3倍ほど速くなります。
for( i = 0 ; i < array.length ; i++ ){ // なんらかの処理 } for(var i = 0 ,len = array.length; i < len ; i++ ){ // なんらかの処理 }
必要とされていないコードをロードしない
必要とされてないJvascriptコードは、ロードさせると処理が遅くなるので、ロードしないようにしましょう。
DOM挿入を実行するときは単一の要素のすべてをラップする
DOMを操作すると非常に遅くなります。極力負荷を与えないように変更してみましょう。
// 負荷のかかるコード $('#header').prepend('<ul id="menu"></ul>'); for (var i = 1; i < 100; i++) { $('#menu').append('<li>' + i + '</li>'); } // 変更後のコード var menu = '<ul id="menu">'; for (var i = 1; i < 100; i++) { menu += '<li>' + i + '</li>'; } menu += '</ul>'; $('#header').prepend(menu);
data()の記述方法
data()
の書き方を少し変えるだけで、速さが変わります。
// 普通の書き方 $(elem).data(key, value); // 10倍速い! $.data(elem, key, value);
jsファイルは一つにまとめる
いくつかのブラウザは同時に2つ以上のスクリプトを処理できません。そのためjsファイルが増えると1つずつ処理するので、順番待ちが発生し、結果的にロード時間を遅くなります。なのでjsファイルを1つにまとめることでロード時間を速くすることができます。
jsファイルを圧縮する
jsファイルを圧縮することで、ロード時間を短縮できます。
jsファイルを圧縮できるサービス
- packer
- JS Minifier
- DHTML JavaScript File Size Compressor
- JavaScript Compressor (CSS Too) – from Creativyst
HTML5を使う
HTML5は軽量なDOM構造の標準を念頭にしています。軽量なDOM構造は、jQueryとロードのパフォーマンスに良い結果をあたえます。可能な場合はHTML5を使用しましょう。
その他、参考サイト
- CDN上のjQueryを使う | Web&Peace
- jQueryのパフォーマンス高速化Tipsまとめ | webプログラマーのメモ
- jQueryのパフォーマンスを下げるアンチパターンに関する超意訳 | Qiita [キータ]
- jQueryの高速化をまとめてみる | じゃばばっとまとめ
javascriptの高速化テク|三浦仮想研究所- 高速で安全なjQueryを書くために今できること | Dress Cording
参考にさせて頂きましたありがとうございます。
最後に
参考になれば幸いですが、検証に使ったコードや計測した方法が必ずしも正しいとは、保証出来ません。上記方法が必ずしも正解ということでもないです。状況によってパフォーマンスが変わることもあります。何か間違いなどあったら教えていただけると嬉しいです。
これからプログラミングを学ぼうと思っているけどどこから学んでいいかわからない方は以下サイトの無料体験レッスンや無料資料請求を試してはいかがでしょうか?一番自分にあいそうなところで学んでみるといいと思います。一人で独学するよりも早く取得できます。
コメントは受け付けていません。