JavaScriptの"Lazy Loading"とレンダリング時間を検証する

先日、YSlow が 2.1.0 にバージョンアップされました。リリースノートを見ると、主に次の2つが目に付きます。

  1. imglinkscriptiframe タグ、あるいは JavaScript の Image() オブジェクト等において、空の srchref 属性をチェックし減点する新たなルール 「画像に対する空の src は避けよ」 を追加した。
  2. head タグ内に動的に script タグを挿入する Deferred Script は、レンダリングを妨げず、ページ内の他要素と平行にダウンロードされるため、ルール 「JavaScript はページの最後に」 の減点対象としないロジックを追加した。

High Performance JavaScript - O'Reilly Media1. は、空の src があった場合、ブラウザがこれを現在のページと解釈し再度リクエストしてしまう結果、トラフィックが倍になってしまうという問題に対するペナルティーとして追加されました。High Performance JavaScript の著者 Nicholas C. Zakas は、2009年11月にこの問題を指摘して以来、WHATWG を通して W3C のメンバーに RFC 3986不備を指摘し、各ブラウザ・ベンダーにも改善を働きかけてきた問題です。現在はベンダー側もこの問題を認知し、対策がとられるようになったようです。

さて、本エントリーでは 2. をメインに取り上げます。ちょうど前回Non-Blocking JavaScript である LABjs というライブラリーを取り上げたばかりなので、私にとってはタイムリーな話題となりました。

1.Deferred Scriptって何?

IE は バージョン4 の時代から、script タグに defer 属性を付けると、body 部のレンダリングが終了するまでスクリプトの実行が遅延され (注:DOM 構築 ~ レンダリングのうち、どこまで 遅延されるのか 現在調査中)、その結果、ページ要素の読み込みをブロックすることなく (注:DOM 構築とレンダリングはブロックしないが、読み込みは相変わらずブロックする) 効率良くページを表示することが出来るようになっていました。IE のこの仕様は HTML 4.01 に採用され、XHTML 1.0 DTDXHTML 1.1 DTD でも定義されています。HTML 4.01 では boolean 属性のため単に <script defer ...> と、XHTML 1.0、1.1 では <script defer="defer" ...> とします。以下は XHTML 1.0 の DTD からの抜粋です。

<!-- script statements, which may include CDATA sections -->
<!ELEMENT script (#PCDATA)>
<!ATTLIST script
  id          ID             #IMPLIED
  charset     %Charset;      #IMPLIED
  type        %ContentType;  #REQUIRED
  src         %URI;          #IMPLIED
  defer       (defer)        #IMPLIED
  xml:space   (preserve)     #FIXED 'preserve'
  >

こちらのページで各ブラウザで defer 属性がどう働くかをテストすることが出来ます。このページでは以下の8つのスクリプトが順に埋め込まれ、実行順にメッセージが表示されます。例えば Inline Head の次に Inline Head Deferred が表示されれば遅延が効いている事になります。

Inline Head Deferred
Inline Head
External Head Deferred
External Head
Inline Body Deferred
Inline Body
External Body Deferred
External Body

IE8、Firefox3.6、Google Chrome1.0 で試した結果を載せておきます。Chrome には defer 属性が実装されていませんし、IE 以外のブラウザもかつては実装されていなかったようです。

IE8

IE8

Firefox3.6

Firefox3.6

Chrome1.0

Chrome1.0

2010年10月25日修正

Chrome 7.0

Chrome 7.0

Chrome 7.0 では外部スクリプトの defer 属性が効きますネ。「Chrome について」 を表示させても 「最新の Chrome です」 となっていたので、7.0 にまで更新されていたのに気付きませんでした :-D 。どのバージョンから実装されたのでしょうか?

この様に Deferred Script の始まりは結構古いのですが、現在では 「スクリプトの実行」 だけではなく、「スクリプトファイルのダウンロード (Deferred Loading)」 を、さらには Ajax 技術と組み合わせてスクリプトの実行やダウンロードを非同期的に、並列に行う技術のことも含んでいるようです。事実 JSDeferred とネーミングされた JavaScript ライブラリは、「JavaScript のコールバックによる非同期処理を直列的に書けるようにするために作られたライブラリ」 と説明されています。ただし、この様に書くとまるでスクリプトがマルチスレッドで実行されるような印象を与えてしまいますが、あくまでも JavaScript はシングルスレッドでの実行が基本ですので、ご注意を ;-)

またスクリプトファイルのダウンロードに限って言えば、本エントリーのタイトルに付けた様に Lazy Loading とも言われるようです。ページをスクロールして必要になったら画像をダウンロードする jQuery のプラグイン 「LazyLoad」 と同じネーミング・センスですね。

参考情報

2.JavaScriptの配置とページの読み込み・レンダリング時間のシミュレーション

ここからが本題です。次のそれぞれを組み合わせたパターンに対し、ページの読み込み時間とレンダリング時間がどうなるかを試してみました。

  • <script> の位置を <head> 内に置くパターンと、</body> の直前に置くパターン
  • <script> で普通にダウンロード/実行するパターンと、LABjs を使いダウンロード/実行するパターン

これらのパターンを Cuzillion でシミュレーションしてみた結果が次の図です。<script> タグによるスクリプトのダウンロードを2秒、実行を2秒に設定し、LABjs は 「script DOM element + after onload」 でモデリングしています。

Cuzillionによるシミュレーション結果

Cuzillionによるシミュレーション結果

この結果から LABjs を使うと 「page load time」 が短くなり、より早く表示されることが分かります。

参考情報

3.実際のページによる検証

iPad風jQueryイメージスライダーをWordPress記事に組み込む」 で使ったサンプルをアレンジして、先の4つのパターンを作成しました。

  1. head without LABjs
  2. head with LABjs
  3. bottom without LABjs
  4. bottom with LABjs

YSlowスコア

「Put JavaScript at bottom」 は冒頭に挙げた YSlow のバージョンアップが効き、パターン01の head without LABjs がグレードB、パターン02の head with LABjs がグレードAとなります。

Firebugウォーターフロー・チャート

LABjs を使った方がブロッキングが少なく、コンテンツである画像の読み込みが終了するまでスクリプトのダウンロードと実行が遅延されています。また DOMContentLoaded イベントが早く発生しており、これは先の Cuzillion によるシミュレーションの結果と良く合います。

01. head w/o LABjs

01. head w/o LABjs

02. head w LABjs

02. head w LABjs

03. bottom w/o LABjs

03. bottom w/o LABjs

04. bottom w LABjs

04. bottom w LABjs

レンダリングの開始時間

各パターンが表示される様子を、「ウェッブサイトの表示速度を測定するフリーツール集」 で紹介した 「WebPagetest – Visual Comparison」 のビデオ機能を使い動画にしてみました。

Please install the Flash Plugin

サーバー負荷やトラフィックの状態で多少の誤差はあるとは思いますが、パターン02 ~ 04は2秒弱でレンダリングが開始されるのに対し、パターン01 は、5秒過ぎまでページが白紙のままで、ちょっと我慢ならない状態となりました。

4.まとめ

以上の検証結果から、JavaScript を <head> に置かざるを得ない場合には、LABjs のような Lazy Loading をするようなライブラリを導入することが、YSlow スコアのアップとページの応答時間の短縮を図る上で非常に効果が高いということが分かりました。

皆さんも導入を検討してみてはいかがでしょうか?

5.その他の参考情報

JavaScriptの"Lazy Loading"とレンダリング時間を検証する」への1件のフィードバック

  1. ピンバック: Tweets that mention ゆっくりと… » JavaScriptの”Lazy Loading”とレンダリング時間を検証する -- Topsy.com

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

海外からの投稿は受け付けていません