ページ読み込み時間を短縮するJavaScriptローダー:LABjs

ブラウザは通常 Web サイトを訪れたとき、CSS や画像などのページ要素を幾つか並列で読み込みますが、JavaScript ファイルを読み込んでいる最中は、(JavaScriptやスタイルシート以外の) 他要素の読み込みをブロックします。それゆえ Yahoo! の 「Best Practices for Speeding Up Your Web Site」 では、それ以上他の要素を読み込む必要のないページ下端に JavaScript を置くことを推奨しているわけです。
これに対し 「ハイパフォーマンスWebサイト―高速サイトを実現する14のルール」 (今は35のルールですが) の著者 Steve Souders が Non-blocking JavaScript
、つまり他のページ要素をブロックしないファイルの読み方を提唱しています。LABjs は氏の監修の下で作成されたオープンソースの JavaScript ライブラリ (MIT ライセンス) です。本エントリーではその使い方と効果を検証してみたいと思います。
動作原理
Steve Souders 氏によれば、Non-blocking Loading
の方法は次の6つがあるそうです。
- XHR Eval
- XHR Injection
- Script in iframe
- Script DOM Element
- Script Defer
- document.write Script Tag
LABjs はこのうち XHR (Ajax) と Script DOM Element とを組み合わせた、非同期 JavaScript ローダーです。またブラウザに他のリソースをロックさせないよう、読み込んだ JavaScript ファイルの MIME タイプを
とし、実行前に text/html
に戻すというトリックを使っているようです。このトリックを念頭においておくことが使いこなしのポイントと思います。text/javascript
使い方
次の様な幾つかの JavaScript ファイルの読み込みと実行を指示する HTML があったとします。
<html>
<head>
<link href="style.css" type="text/css" media="screen" />
<script src="jquery.js" type="text/javascript"></script>
<script src="jquery.ui.js" type="text/javascript"></script>
<script src="jquery.lightbox.js" type="text/javascript"></script>
</head>
<body>
...
<div id="gallary">
<!-- ギャラリーアイテム -->
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#gallary').lightbox();
});
</script>
...
</body>
</html>
これに LABjs を適用するには
に必要な JavaScript ファイルを指定して連結します。.script()
<html>
<head>
<link href="style.css" type="text/css" media="screen" />
<script src="LAB.js" type="text/javascript"></script>
<script type="text/javascript">
$LAB
.script("jquery.js").wait()
.script("jquery.ui.js")
.script("jquery.lightbox.js")
.wait(function() {
$(document).ready(function() {
$('#gallary').lightbox();
});
});
</script>
</head>
<body>
...
<div id="gallary">
<!-- ギャラリーアイテム -->
</div>
...
</body>
</html>
LABjs では .script() で連結されたファイルは非同期に並列で読み込まれることに注意してください。ゆえに JavaScript 相互に依存関係がある場合には .wait() によって同期を取らなければなりません。上の例では、jquery.ui.js と jquery.lightbox.js は jquery.js の読み込みが終了していない場合はエラーを起こすので、.wait() を指定して同期の保証をします。LABjs 適用前でも Firefox などのモダンブラウザでは JavaScript ファイルを並列に読み込みますが、ブラウザは読み込むべき順番を知っているので、同期的に依存関係を解決してくれていたわけです。
またスクリプトの実行は、一連の連結の後にやはり wait() の引数内を通して指定されなければなりません。「動作原理」に書いたトリックを思い出してください。通常のインライン・スクリプト
<script type="text/javascript">
$(document).ready(function() {
$('#gallary').lightbox();
});
</script>
では、前述の非同期ローディングのトリックを解決できないため、実行が出来ないのです。
使用上の注意
LABjs にはこれ以外にも幾つかの注意事項があります。
LABjs 自体のローディング
Snippet to load LABjs itself dynamically にあるコード・スニペットをインラインのスクリプトとして記述すれば OK です。お勧めの構成は、導入ページにインライン・スクリプトを配置し、次ページ以降は通常の <script> タグで読み込むことです。こうすればキャッシュから読み込むことになるからです。
ユーザーのエクスペリエンス
従来ページに LABjs を適用すると、コンテンツが表示される前にスクリプトが読み込まれ実行される可能性があります。これにより、JavaScript でコンテンツのスタイルを変えているようなページの場合、スタイル適用前の生のコンテンツが見えてしまったりします。つまり LABjs 適用後は各ブラウザでどう見えるかを考える必要があるのです。この考え方は 「プログレッシブ・エンハンスメント」 (古いタイプのブラウザにも必要機能を提供し、より革新的なブラウザにはリッチなコンテンツを提供すること) によくマッチします。つまり、より 「プログレッシブ」 の部分が際立つのです。しかしながら、多くのスタイルをスクリプトで変えているようなサイトの場合、その修正は大変です。こんなときは、CSS で display: none あるいは visibility: hidden 等によりスクリプトによって再レンダリングされる全てのコンテンツを隠すと良いでしょう。もちろん、スクリプトを有効にしていないユーザーのために <noscript> で画されたコンテンツを表示するように CSS を指定することを忘れてはいけません。ただし Valid な HTML にはなりませんが…。
document.write()と一緒には使えない
LABjs は非同期ローダーですが、document.write() は本来同期的であるため、LABjs の非同期技術を壊してしまいます。appendChild() 等を使って DOM 要素を追加するスクリプトに変えなければなりません。
安全でないDOM-ready状態を検知する特定のJavaScriptフレームワーク
jQuery 1.4 未満は DOM-ready の発生以降にスクリプトが読み込まれた場合、2度と DOM-ready が発生しないため、スクリプトが実行されないという不具合があります。これは Jquery のバグとして報告されていて、バージョン 1.4 で修正されるはずだと書いてあります。 $(document).ready(function(){…}); を使うスクリプトは注意しましょう。
ローカル環境でのテスト
LABjs は XHR を使うため、ローカルではテストが出来ません。そこで LABjs をダウンロード/解凍して出来る fLAB.js を使います。fLAB.js は LAB.js のラッパーとして機能します。
<script src="LAB.js" type="text/javascript"></script> <script src="fLAB.js" type="text/javascript"></script> <!-- 開発環境のみ -->
効果検証
jQuery 1.4.3 と Nivo Slider、lightbox を使ったサンプルを用意しました。サンプル3 はオマケです。サーバーやネットワークの状況にもよりますが、「使用上の注意」 で述べたように DOM-ready が検知でずにスライドショーが始まらない事象が頻発すると思います。
- サンプル1 (LAB 適用前)
- サンプル2 (LAB 適用後)
- サンプル3 (LAB 適用後 jQuery 1.3.2)
- サンプル4 (サンプル1 の jQuery 1.4.2 版)
- サンプル5 (サンプル2 の jQuery 1.4.2 版)
それぞれのYSlow のウォータフォール図を並べてみると、ページ全体の読み込み時間に差はありませんが、メインコンテンツである画像を読み込む時間 (DOMContentLoaded) は LAB 適用前が 820msec であるのに対し、LAB 適用後が 260msec となり、体感できるほどに短縮できていることが分かります。
また LABjs 導入前後におけるレンダリングの様子を、「WebPagetest – Visual Comparison」 でビデオにしてみました。LABjs 導入後の方が、描画が早く開始している事がよくわかります (2011年5月31日追記)。
スクリプトのローディングとレンダリングのブロックについては、以下の記事にも詳しく書きましたので、参照して下さい。
2010年10月18日追記
余談ですが、サンプル1、2 はIE6でエラーが出ることが分かりました。画像自体は切り替わるのですが、切り替わり時のエフェクトが動作しません。そこでサンプル4、5 として jQuery 1.4.2 版もアップすることにしました。Nivo Slider を使うときは 1.4.3 は使わない方がよいかも知れません。
参考情報
JavaScriptローダー関連
Steve Souders
- Steve Souders – High Performance Web Sites
Even Faster Web Sites – O’Reilly Media
2009年7月
Web 2.0 Expo: Even Faster Web Sites
2007年4月15日
SXSW: Even Faster Web Sites
2009年3月14日


High Performance Kick Ass Web Apps (JavaScript edition)
Don’t make me wait! or Building High-Performance Web Applications
Progressive Downloads and Rendering
最近のコメント