ふろしき.js

Web + Mobile + UX + Performance Tech

1300の優良サイトが選んだ「AMD・遅延ロード」のベストプラクティス

1300の優良サイトが選んだシリース、第3回は「AMDと遅延ロード」です。

JavaScriptファイルのロードのパフォーマンス向上を図るメカニズムを「AMD(Asynchronous module definition)」といいます。Webアプリ開発を行なうデベロッパの方は、今後重要な知識になるでしょう。

一方で画像ファイルのパフォーマンスを向上させようという「遅延ロード(Lazy Load)」というアイデアもあります。こちらはサイトのデザインそのものにも影響を与えることになるため、Webアプリのデベロッパだけでなくデザイナの方も、仕組みを知る必要があるでしょう。

今回も、1300の優良サイトが採用しているAMD・遅延ロード手法を確認し、ベストプラクティスが何かを探ってみようかと思います。

画像ファイルの非可視エリアロード問題対策

画像ファイルのサイズは、比較的大きくなるという特徴を持ちます。そして対策を行わなかった場合、HTMLドキュメントのロードと同時に一斉にロードを始めて、サーバやネットワークのリソースを圧迫します。

しかしこれらの画像ファイルは、ロード直後にすぐ必要になるとは限りません。Webページの下部に配置された画像ファイルは、ユーザの目に触れることすら無い可能性があります。

こうしたWebページの特性に合わせて画像ロードをさせようというアイデアが、「Lazy Load(遅延ロード)」です。Webページ表示後、ユーザビリティを損なわない任意のタイミングで画像ファイルをロードさせることで、サーバやネットワークの負荷を最適化します。

f:id:furoshiki0223:20131014014812p:plain

1300中、5つのサイト(0.38%)にて、JSライブラリによる対策が確認されました。

★ Lazyload (5件)

JavaScriptの直列実行問題対策

画像ファイルは並列にロードすることでWebページ表示速度を改善しようとしますが、JavaScriptの場合はそうもいきません。JavaScriptファイル間に依存性を生じることがあるからです。

対策を行っていない場合、全てのJavaScriptファイルは直列にロードされます。一つずつ読み込みを行い、コンパイルを行ない、実行するという手順を踏みます。大規模な場合、Webページの初期表示のパフォーマンスの低下に繋がることがあります。

JavaScriptにもLazy Load(遅延ロード)という考え方があります。既にW3CWeb標準にも組み込まれており、JavaScriptファイルの読み込みを行なうためのscript要素から、defer属性を通じて制御することができます。

<script src="xxx.js" defer></script>

また、最近は並列ロード・実行というアイデアにも注目を集めています。JavaScriptの依存関係を予め定義しておき、できる限りの並列実行を行おうというアプローチです。これを「AMD(Asynchronous module definition)」と呼び、CommonJSと呼ばれるデファクト標準でも扱われているテーマです。

1300中、14サイト(1.07%)にてJSライブラリによる対策が確認されました。

★ require.js (11件)

★ load.js (2件)

★ HeadJS (1件)

  • 公式サイト:http://headjs.com/
  • ライセンス:MIT License
  • 開発:Tero Piirainen

Modernizr(33件)のloadメソッドにもyepnope.js(0件)と互換のAPIを持ち、またSenchaフレームワーク(1件)やYeoman系フレームワーク(1件)にも活用されています。このため、上記の一覧が全てであるとは言い切れません。

ただ、単体での利用では、CommonJS準拠であり多くのフレームワークでも活用されている「require.js」がベストプラクティスであると判断できます。

CSSファイルとFOUC問題対策

CSSファイルは、JavaScriptや画像とはまた違った特性の問題を持ちます。

Webページを読み込み時、WebブラウザはHTMLドキュメントの内容を読み取り、逐次「レンダーツリー」という見た目の情報を整理します。

WebページはHTMLドキュメントを全て読み込んだ後に一括で表示するのではなく、読み込みを行いながら逐次Webブラウザのビューポート(コンテンツの表示エリア)に表示していくことで、体感速度の改善を図っているのです。

レンダーツリーを含む様々なツリー構築プロセスは「フロー処理」と呼びますが、その処理にはCSSプロパティ情報を必要とします。フロー処理完了後に遅延させてCSSファイルを読み込んでしまうと、これまで構築したレンダーツリー内で、影響のある範囲の情報を全て再修正を行なうことになり、フロー処理のパフォーマンスを劣化させることになります。

f:id:furoshiki0223:20131014021125p:plain

また、HTMLドキュメントの読み込み完了後からCSSファイルの読み込み完了までの間、ビューポート上にはCSSプロパティが適用されていない状態で表示されることになります。

非常に小さなファイルであれば気になりませんが、大規模なものになると読み込み直後に不格好な画面を一瞬だけユーザに見せてしまうことになります。これを「FOUC(Flash of Unstyled Content)」と呼び、一般的にはアンチパターンとして扱っています。

f:id:furoshiki0223:20131014021659p:plain

上記の特性から、CSSファイルのロード処理を最高のパフォーマンスで動作させることができるのは、フロー処理が開始される前です。

フロー処理は「フローコンテンツ」の読み込みによって行われ、これらはbody要素内に含めて記述するのがWeb標準のお約束です。つまり、head要素内で行うのが、ベストプラクティスです。

<head>
    ・・・
    <link rel="stylesheet" href="xxx.css" />
</head>
<body>
    ・・・
</body>

ただし、特定の条件を満足させた場合のみ特定のCSSファイルの内容を反映させたいというケースがあります。CSSファイルの読み込みはbody要素内で読み込んでも問題無いため、この場合例外的に遅延ロードは許容するというお作法があります。

多くの場合、JSライブラリを経由するのが一般的で、HTMLドキュメントのエディタは、CSSファイルが遅延ロードされていることを意識するようなことはありません。1300中17つのサイト(1.30%)にて対策が確認されました。

★ styleswitcher.js (17件)

注意点

画像の遅延ロードについては、PNG Fix対策と競合するため注意が必要です。遅延ロード対象は写真・絵でJPGフォーマットのみに限定し、PNG Fix対象はアイコン・ロゴをPNGフォーマットで縛るといった、用途ごとの使い分けが求められます。

Lazyloadは予め画像の縦・横サイズをimg要素に明示しないと、無駄なフロー処理を発生させることになります。このため、ユーザがアップロードした画像に対して利用する場合、サーバ側で画像の解析を行なう必要があります。こうした問題を解決してでも、対策が必要と感じる時に利用すべきです。

JavaScriptAMDは、元はサーバサイドJavaScriptが発祥で、DHTMLベースRIAの実装技術として輸入されたものです。JavaScriptであまり複雑なことをさせない場合は、オーバヘッドが大きいためお勧めできません。script要素のdefer属性を利用する程度にしておくのが良いでしょう。

多くは、Webの進化で発生した問題への対応策で、正攻法とは言い難いです。Web技術を扱うプレイヤーはとても多様なので、ドメインを履き違えると逆に問題を大きくしてしまいます。何に対する対策なのかを明確にして、実装の可否を判断は慎重に行なうようにして下さい。