メインコンテンツへ移動

フロントエンド最適化:Yahoo! 35のルール

無料2015-04-03#Mind#前端优化#雅虎35条

1週間前から英語検定の小さなブラシ(筆)を手にヤフーの35のルールを翻訳し始め、今日ついに完成しました。フロントエンドの仲間の皆さんにぜひ査読していただきたいです。[聞いたことがある < 知っている < 把握している < 理解している < 使ったことがある]、一度じっくり読めば少なくとも「把握している」段階には到達できるはずです。

no_mkd

はじめに

英語版オリジナルリンク。もし本文に不備があれば、速やかに修正するためにご指摘ください。

目次(7つのカテゴリ、計35項目):

  1. [コンテンツ] HTTPリクエスト数を最小限にする
  2. [サーバー] CDN(Content Delivery Network)を使用する
  3. [サーバー] ExpiresまたはCache-Control HTTPヘッダーを追加する
  4. [サーバー] コンポーネントをGzip圧縮する
  5. [CSS] スタイルシートを上部に配置する
  6. [JS] スクリプトを下部に配置する
  7. [CSS] CSS式(Expression)の使用を避ける
  8. [JS, CSS] JavaScriptとCSSを外部ファイル化する
  9. [コンテンツ] DNSルックアップを削減する
  10. [JS, CSS] JavaScriptとCSSを最小化(Minify)する
  11. [コンテンツ] リダイレクトを避ける
  12. [JS] 重複するスクリプトを削除する
  13. [サーバー] ETagsを構成する
  14. [コンテンツ] Ajaxをキャッシュ可能にする
  15. [サーバー] バッファを早めにフラッシュする
  16. [サーバー] AjaxにはGETリクエストを使用する
  17. [コンテンツ] コンポーネントの後回し読み込み(Lazy Load)
  18. [コンテンツ] コンポーネントをプリロードする
  19. [コンテンツ] DOM要素の数を削減する
  20. [コンテンツ] コンポーネントを複数のドメインに分散させる
  21. [コンテンツ] iframeの使用を最小限にする
  22. [コンテンツ] 404エラーをなくす
  23. [Cookie] Cookieのサイズを小さくする
  24. [Cookie] コンポーネントをCookieのないドメインから提供する
  25. [JS] DOMアクセスを最小限にする
  26. [JS] 賢いイベントハンドラを使用する
  27. [CSS] @importよりも<link>を選択する
  28. [CSS] フィルタの使用を避ける
  29. [画像] 画像を最適化する
  30. [画像] CSSスプライトを最適化する
  31. [画像] HTMLで画像をスケーリングしない
  32. [画像] 小さくキャッシュ可能なfavicon.icoを使用する(P.S. お気に入りアイコン)
  33. [モバイル] すべてのコンポーネントを25K未満にする
  34. [モバイル] コンポーネントを1つの複合ドキュメントにパックする
  35. [サーバー] 画像のsrc属性を空にしない

翻訳:ウェブサイトを高速化するためのベストプラクティス

私たちは、サイトを高速化するための多くのベストプラクティスを発見しました。それらは7つのカテゴリ、計35項目に分けられます。

1. HTTPリクエスト数を最小限にする

カテゴリ: コンテンツ

エンドユーザーの応答時間の80%はフロントエンドに費やされており、その大部分はページ上の様々なコンポーネント(画像、スタイルシート、スクリプト、Flashなど)のダウンロードに費やされています。コンポーネントの数を減らすことは、必然的にページから送信されるHTTPリクエスト数を減らすことになります。これがページを高速化する鍵です。

コンポーネント数を減らす一つの方法はデザインをシンプルにすることですが、複雑なページを構築しつつ応答時間を短縮する方法はあるのでしょうか?はい、両立させる方法はあります。

ファイルの結合は、すべてのスクリプトを一つのファイルにまとめることでリクエスト数を減らす方法です。同様にCSSも結合できます。ページごとにスクリプトやスタイルが異なる場合、結合は手間のかかる作業になりますが、これをリリースプロセスの一部に組み込むことで応答時間を改善できます。

CSSスプライトは、画像リクエスト数を減らすための好ましい方法です。背景画像を一枚の画像に統合し、CSSのbackground-imagebackground-positionプロパティを使って表示したい部分を指定します。

イメージマップは、複数の画像を一枚に結合できます。全体のサイズは変わりませんが、リクエスト数が減りページの読み込みが速くなります。イメージマップは、ナビゲーションバーのように画像がページ内で連続している場合にのみ有効です。座標設定は退屈でミスを誘発しやすく、ナビゲーションとしての利用も容易ではないため、この方法は推奨されません。

インライン画像(Base64エンコード)data: URLスキームを使って画像をページに埋め込みます。これによりHTMLファイルが大きくなりますが、インライン画像を(キャッシュされた)スタイルシート内に配置することで、ページが重くなるのを避けつつリクエストを削減できます。ただし、主要なブラウザがインライン画像を完全にはサポートしていない場合があります。

HTTPリクエスト��を減らすことは出発点であり、初回訪問時の速度を向上させるための重要な指針です。Tenni TheurerのブログBrowser Cache Usage - Exposed!にあるように、訪問者の40%から60%はキャッシュが空の状態でサイトに訪れます。そのため、初回訪問の高速化はユーザーエクスペリエンス向上に極めて重要です。

2. CDN(Content Delivery Network)を使用する

カテゴリ: サーバー

ユーザーとサーバーの物理的な距離も応答時間に影響します。コンテンツを地理的に分散した複数のサーバーに配置することで、ユーザーはより速くページを読み込めます。しかし、具体的にどうすればよいのでしょうか?

地理的分散を実現するための第一歩は、分散構造に合わせてWebアプリケーションを再設計しようとしないことです。アプリケーションによっては、セッション状態の同期やデータベーストランザクションの複数サーバーへのレプリケーションといった困難なタスクが発生する可能性があります。ユーザーとコンテンツの距離を縮める提案が、この難題のために延期されたり、不可能と判断されたりすることがあります。

エンドユーザーの応答時間の80%から90%は、画像、スタイル、スクリプト、Flashなどのコンポーネントのダウンロードに費やされていることを忘れないでください。これがパフォーマンスの黄金律です。最初からアプリケーション構造を再設計するのではなく、まずは静的コンテンツを分散させるのが最善です。これは応答時間を大幅に短縮するだけでなく、CDNのメリットも示しやすいです。

コンテンツ配信ネットワーク(CDN)は、地理的に分散したWebサーバーのグループで、ユーザーに効率的にコンテンツを送信するために使用されます。通常、コンテンツを送信するサーバーは、ネットワーク上の距離(ホップ数が最小、または応答時間が最速など)に基づいて選択されます。

巨大なインターネット企業の中には独自のCDNを持っているところもありますが、Akamai TechnologiesEdgeCast、またはLevel3などのCDNサービスプロバイダーを利用するのがコスト効率が良いでしょう。スタートアップや個人サイトにとってCDNのコストは高いかもしれませんが、ユーザーベースが拡大しグローバル化するにつれ、高速な応答時間のためにCDNを利用する必要性は高まります。Yahoo!では、静的コンテンツをアプリケーションサーバーからCDN(サードパーティ製および自社製を含む)に移動することで、エンドユーザーの応答時間を20%以上改善できました。CDNへの切り替えは比較的単純なコード変更ですが、サイトの応答速度を劇的に向上させます。

3. ExpiresまたはCache-Control HTTPヘッダーを追加する

カテゴリ: サーバー

このルールには2つの側面があります:

  • 静的コンポーネントに対して:遠い将来の日付をExpiresに設定して期限切れにならないようにする
  • 動的コンポーネントに対して:適切なCache-ControlHTTPヘッダーを使用してブラウザに条件付きリクエストを行わせる

ウェブデザインは年々豊かになっており、ページ内にはより多くのスクリプト、画像、Flashが含まれるようになっています。初回訪問者は依然としていくつかのHTTPリクエストを送信しなければなりませんが、有効期限を設定してコンポーネントをキャッシュ可能にすることで、その後の閲覧における不要なHTTPリクエストを避けることができます。Expiresヘッダーは通常画像に使われますが、スクリプト、スタイル、Flashコンポーネントを含むすべてのコンポーネントに使用されるべきです。

ブラウザ(およびプロキシ)はキャッシュを使用してHTTPリクエストの数とサイズを減らし、ページの読み込みを高速化します。WebサーバーはExpiresヘッダーを通じて、各コンポーネントをどれくらいの期間キャッシュすべきかをクライアントに伝えます。遠い将来の日付を設定することで、ブラウザに「このレスポンスは2010年4月15日まで変わらない」と伝えます。

Expires: Thu, 15 Apr 2010 20:00:00 GMT

Apacheサーバーを使用している場合は、ExpiresDefault指令を使用して現在の日付からの相対的な有効期限を設定できます。以下の例では、リクエスト時から10年間の有効期限を設定しています:

ExpiresDefault "access plus 10 years"

遠い将来の日付を設定する場合、コンポーネントが変更された際にファイル名を変更しなければならないことに注意してください。Yahoo!では、ビルドプロセスの一環として、ファイル名にバージョン番号を埋め込んでいます(例:yahoo_2.0.6.js)。

遠い将来のExpiresヘッダーを設定しても、効果があるのはユーザーが一度訪問した後です。初回訪問者やキャッシュをクリアした場合にはHTTPリクエスト数に影響しません。そのため、このパフォーマンス向上はキャッシュを持つユーザーの訪問頻度に依存します。私たちはYahoo!でこのデータを測定し、コンポーネントがキャッシュされた状態でのページビュー(PV)が75%から85%であることを発見しました。遠い将来のExpiresを設定することでブラウザにキャッシュされるコンポーネントが増え、後続のアクセスで1バイトも追加で送信する必要がなくなります。

4. コンポーネントをGzip圧縮する

カテゴリ: サーバー

フロントエンドエンジニアは、ネットワーク経由でのHTTPリクエストとレスポンスの転送時間を大幅に短縮する方法を見つけることができます。エンドユーザーの帯域幅、ISP、ピアリングポイントからの距離などは開発チームがコントロールできるものではありませんが、他にも応答時間に影響を与える要因があります。圧縮はHTTPレスポンスのサイズを小さくすることで応答時間を短縮できます。

HTTP/1.1以降、Webクライアントは圧縮をサポートするAccept-Encoding HTTPリクエストヘッダーを持つようになりました。

Accept-Encoding: gzip, deflate

Webサーバーがこのヘッダーを検知すると、クライアントがリ��トした方法の一つでレスポンスを圧縮します。サーバーはContent-Encodingレスポンスヘッダーを通じてクライアントに通知します。

Content-Encoding: gzip

Gzipは現在、最も一般的で効率的な圧縮方法であり、GNUプロジェクトによって開発されRFC 1952として標準化されています。他にもdeflateという形式を見かけることがありますが、効率が悪く一般的ではありません。

Gzip圧縮は通常、レスポンスを約70%圧縮でき、現在ブラウザを介したネットワーク転送の約90%がGzipをサポートしています。Apacheサーバーの場合、Gzipを構成するモジュールはバージョンによって異なり、Apache 1.3はmod_gzip、Apache 2.xはmod_deflateモジュールを使用します。

ブラウザやプロキシの要因により、期待される圧縮内容と実際に受け取る内容が一致しない場合があります。幸い、古いブラウザの淘汰に伴いこうしたケースは減少しており、Apacheモジュールは適切なVaryヘッダーを自動追加することでこれを解決できます。

サーバーはファイルタイプに基づいてGzip圧縮するかどうかを決定しますが、これは限定的です。多くのサイトはHTMLファイルをGzip圧縮しますが、スクリプトやスタイルシートも圧縮の対象として最適です。実際、XMLやJSONを含むあらゆるテキストコンテンツを圧縮できますが、画像やPDFはすでに圧縮されているため、Gzipで再圧縮するとCPUを浪費するだけでなく、サイズが大きくなる可能性もあります。

可能な限りGzip圧縮を利用してページを軽量化することは、ユーザーエクスペリエンスを向上させる最も簡単な方法です。

5. スタイルシートを上部に配置する

カテゴリ: CSS

Yahoo!でのパフォーマンス研究により、スタイルシートをドキュメントのHEADセクションに配置すると、ページが速く読み込まれるように見えることがわかりました。これは、スタイルシートをHEADに置くことで段階的なレンダリングが可能になるためです。

パフォーマンスを重視するフロントエンドエンジニアは、段階的レンダリング(プログレッシブレンダリング)を望みます。つまり、ブラウザにできるだけ早く既存のコンテンツを表示させたいのです。これは、ページに大量のコンテンツがある場合やユーザーの回線が遅い場合に特に重要です。ユーザーへのフィードバック(進行状況のインジケーターなど)の重要性は広く研究されており、文書化されています。この場合、HTMLページそのものが進行状況のインジケーターなのです!ブラウザがヘッダー、ナビゲーション、ロゴなどを順次読み込む際、それらは待機しているユーザーへのフィードバックとなり、全体的なユーザーエクスペリエンスを高めます。

多くのブラウザ(IEを含む)では、スタイルシートをHTMLドキュメントの底部に配置すると、段階的レンダリングが妨げられます。これらのブラウザは、スタイルの変更による再描画を避けるためにレンダリングをブロックし、ユーザーは空白のページを見ることになります。

HTML公式ドキュメントでは、スタイルシートはHEAD内に配置すべきだと明確に述べられています:"Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times."(Aタグとは異なり、LINKタグはドキュメントのHEADセクションにのみ出現できます。ただし、何度でも出現可能です)。空白の画面やスタイルのないフラッシュコンテンツ(FOUC)は避けるべきです。理想的な解決策は、公式ドキュメントに従い、スタイルシートをHEAD部分に配置することです。

6. スクリプトを下部に配置する

カテゴリ: JavaScript

スクリプトは並列ダウンロードをブロックします。HTTP/1.1の仕様では、ブラウザがホスト名ごとに並列にダウンロードするコンポーネント数を2つまでにすることを推奨しています。画像が複数のホスト名から提供されている場合、並列ダウンロード数は2つを超えられますが、スクリプトのダウンロード中、ブラウザは他のホスト名からのものであっても、他のダウンロードタスクを開始しません。

スクリプトを底部に移動させるのが簡単ではない場合もあります。例えば、document.writeを使ってコンテンツを挿入している場合、それ以上下には移動できません。スコープの問題もありますが、多くの場合これらは解決可能です。

一般的な提案としてDEFER属性を使用する方法があります。DEFER属性を持つスクリプトはdocument.writeを含んではならず、ブラウザにレンダリングを続行できることを伝えます。不運にもFirefoxはDEFER属性をサポートしておらず、IEでも期待通りの挙動にならないことがあります。可能であればスクリプトはページ底部に配置すべきであり、そうすることでページ読み込みが速くなります。

7. CSS式(Expression)の使用を避ける

カテゴリ: CSS

CSS式を使用して動的にCSSプロパティを設定することは、強力ですが危険な手法です。IE5からサポートされていましたが、IE8以降は非推奨です。例えば、CSS式を使って背景色を1時間ごとに切り替えることができます:

background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

上記のコードでは、expressionメソッドがJavaScriptの式を受け取ります。CSSプロパティは式の計算結果に設定されます。このメソッドは他のブラウザでは無視されるため、IE独自のクロスブラウザ対応が必要な場合にのみ使用されていました。

式における最大の問題は、想像以上に頻繁に再計算されることです。レンダリング時やウィンドウのサイズ変更時だけでなく、スクロール中や、ユーザーがマウスを動かしている間も再計算されます。カウンターを付けて確認すると、マウスを動かすだけで1万回以上の再計算が発生することもあります。

再計算を減らす一つの方法は「使い切り」の式にすることです。つまり、最初の計算後にプロパティを固定値に置き換えて式を削除します。ページのライフサイクル全体で動的に設定が必要な場合は、CSS式の代わりにイベントハンドラを使用してください。どうしても使用する場合は、数千回再計算される可能性があることを念頭に置いてください。

8. JavaScriptとCSSを外部ファイル化する

カテゴリ: JavaScript, CSS

多くのパフォーマンス原則は外部コンポーネントの管理に関するものですが、その前に根本的な問いを立てるべきです:JavaScriptとCSSは外部ファイルにすべきか、それともページ内に直接記述(インライン)すべきか?

実際には、外部ファイルを使用する方がページは速くなります。JavaScriptやCSSファイルがブラウザにキャッシュされるためです。インラインで記述すると、HTMLドキュメントをリクエストするたびにそれらも再ダウンロードされます。インラインはHTTPリクエスト数を減らしますが、HTMLドキュメントのサイズを大きくします。一方で、外部ファイル化してキャッシュされていれば、リクエスト数を増やすことなくHTMLドキュメントを小さく保つことができます。

鍵となるのは、キャッシュの頻度とページのリクエスト数の関係です。これを定量化するのは難しいですが、様々な指標で測定できます。ユーザーが1回のセッションで複数回ページを訪れる場合、同じスクリプトやスタイルシートを再利用できるため、外部ファイルによるキャッシュは大きなメリットをもたらします。

多くのサイトはこの中間に位置しており、一般的な解決策は外部ファイルとしてデプロイすることです。唯一の例外は、Yahoo!のトップページMy Yahoo!のように、セッションあたりの訪問回数が少ないトップページです。こうしたページではインラインの方が応答時間が短くなる場合があります。

典型的なサイトにおいて、トップページは多くの訪問の入り口です。初回はインラインを使用し、ページの読み込み完了後に動的に外部ファイルを読み込んでキャッシュさせる技術など、HTTPリクエスト削減とキャッシュのメリットを両立させる手法もあります。

9. DNSルックアップを削減する

カテゴリ: コンテンツ

DNSはホスト名とIPアドレスのマッピングを行います。ブラウザにURLを入力すると、DNSリゾルバに問い合わせてサーバーのIPアドレスを取得します。DNSにはコストがかかり、通常20〜120ミリ秒を要します。これが完了するまでブラウザは何もダウンロードできません。

DNSルックアップは、ISPやローカルネットワーク、そして個人のコンピュータのOS(Windowsの「DNS Clientサービス」など)にキャッシュされます。また、多くのブラウザもOSとは独立したキャッシュを持っています。ブラウザが独自のキャッシュを保持している間は、OSに問い合わせることはありません。

IEはデフォルトで30分間(DnsCacheTimeout設定)、Firefoxは1分間(network.dnsCacheExpiration設定)キャッシュします。

キャッシュが空の場合、DNSルックアップの回数はページ内の異なるホスト名の数に等しくなります。ホスト名の数を減らせばDNSルックアップを削減できますが、一方で並列ダウンロードできるコンポーネントの数も減ってしまいます。私の推奨は、2〜4つのホスト名に分散させることです。これはルックアップの削減と高並列ダウンロードの妥協点です。

10. JavaScript和CSSを最小化(Minify)する

カテゴリ: JavaScript, CSS

最小化とは、コードから不要な文字を取り除いてサイズを縮小し、読み込み速度を向上させることです。コメントや不要な空白(スペース、改行、タブ)を削除します。JavaScriptではファイルが小さくなるため応答性が向上します。代表的なツールにはJSMinYUI Compressorがあり、後者はCSSも圧縮できます。

難読化(Obfuscation)はさらに複雑な最適化ですが、バグを誘発しやすくなります。米国上位10サイトの調査では、最小化により21%削減でき、難読化では25%削減できることがわかりました。削減率は高いですが、リスクも大きくなります。

外部ファイルだけでなく、インラインの<script><style>ブロックも最小化すべきです。Gzipを有効にしていても、事前に最小化することでさらに5%以上のサイズ削減が期待できます。

11. リダイレクトを避ける

カテゴリ: コンテンツ

リダイレクトは301や302ステータスコードを使用します:

HTTP/1.1 301 Moved Permanently
      Location: http://example.com/newuri
      Content-Type: text/html

ブラウザはLocationで指定されたURLに自動的にジャンプします。リダイレクトに必要な情報はヘッダーに含まれ、ボディは空であることが多いです。リフレッシュタグやJavaScriptによる遷移もありますが、リダイレクトが必要な場合は、戻るボタンを正常に機能させるために標準の3xxコードを使用するのが最善です。

リダイレクトはユーザー体験を著しく低下させます。HTMLドキュメントがブラウザに届くまで、ページのレンダリングもコンポーネントのダウンロードも開始されないため、すべてが遅延します。

非常に一般的で無駄なリダイレクトは、URL末尾のスラッシュ(/)がない場合に発生するものです。例えば、.../astrologyへのアクセスは、末尾にスラッシュのあるURLへの301レスポンスを返します。ApacheではAliasmod_rewriteDirectorySlash指令を使用してこれを回避できます。

リダイレクトの主な用途は古いサイトから新しいサイトへの誘導ですが、Aliasmod_rewriteを使用すれば、同じサーバー内であればリダイレクトなしで解決できます。ドメイン変更に伴う場合は、CNAMEレコードとこれらの指令を組み合わせることも検討してください。

12. 重複するスクリプトを削除する

カテゴリ: JavaScript

ページ内に重複したスクリプトファイルが含まれていると、パフォーマンスに悪影響を与えます。米国上位10サイトの調査では、2つのサイトで重複が見られました。重複の原因はチームの規模やスクリプトの多さです。重複は不要なHTTPリクエストを生み、無駄なコード実行を引き起こします。

IEは不要なHTTPリクエストを発生させますが、Firefoxは発生させません。IEにおいてキャッシュ不可のスクリプトが2回読み込まれると、2つのリクエストが発生します。キャッシュ可能であっても、リロード時には余分なリクエストが発生します。

リクエストだけでなく、コードの評価にも時間がかかります。FirefoxとIEのどちらも、キャッシュの有無にかかわらず、重複したコードを実行してしまいます。

これを防ぐ一つの方法は、テンプレートシステムでスクリプト管理モジュールを実装することです。典型的な読み込み方法はタグを使用することですが:

<script type="text/javascript" src="menu_1.0.17.js"></script>

PHPでの代替案として、insertScriptのような関数を作成することが挙げられます:

<?php insertScript("menu.js") ?>

この関数により、重複防止、依存性チェック、バージョン管理による「永久」キャッシュのサポートなどが可能になります。

13. ETagsを構成する

カテゴリ: サーバー

エンティティタグ(ETags)は、サーバーとブラウザがキャッシュ内のコンポーネントがオリジンサーバーのものと一致するかを判断する仕組みです。ETagは特定のバージョンの唯一の識別子となる文字列です。オリジンサーバーはレスポンスヘッダーのETagで指定します:

HTTP/1.1 200 OK
      Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
      ETag: "10c24bc-4ab-457e1c1f"
      Content-Length: 12195

ブラウザがコンポーネントを検証する際、If-None-MatchヘッダーでETagをサーバーに送ります。一致すれば304ステータスコードが返り、ボディの転送(12195バイト)が節約されます。

ETagの問題は、特定のサーバーごとに生成されることです。複数のサーバーで構成されるサイトでは、あるサーバーで取得したコンポーネントを別のサーバーで検証しようとしてもETagが一致せず、有効性テストが失敗します。ApacheやIISのデフォルト設定では、サーバー固有の情報が含まれるため、多サーバー構成での成功率は極めて低くなります。

Apache 1.3/2.xの形式はinode-size-timestampです。同じファイルでもサーバーが異なればinode番号が異なるため一致しません。IIS 5.0/6.0も同様にFiletimestamp:ChangeNumber形式であり、サーバー間でChangeNumberが一致することはありません。

結果として、本来304で済むはずのリクエストが、すべてのデータを伴う200レスポンスになってしまいます。シングルサーバーなら問題ありませんが、マルチサーバー構成でデフォルト設定のままの場合、ページの読み込みが遅くなり、サーバー負荷と帯域消費が増大します。永久的なExpiresを設定していても、リロード時には条件付きGETリクエストが発生します。

ETagの柔軟な検証モデルが不要であれば、ETagを完全に削除するのが最善です。Last-Modifiedヘッダーによる検証に頼ることで、ヘッダーのサイズを減らすこともできます。Apacheでは以下の設定で無効化できます:

FileETag none

14. Ajaxをキャッシュ可能にする

カテゴリ: コンテンツ

Ajaxの利点は即時フィードバックですが、非同期リクエストが返ってくるまでユーザーが待たされることに変わりはありません。「非同期」は「即時」を意味しないため、Ajaxレスポンスを最適化することは非常に重要です。

最も重要な方法は、ExpiresまたはCache-Controlヘッダーを使用してキャッシュ可能にすることです。その他、以下のルールも適用されます:


例えば、オートコンプリート機能のために連絡先リストをAjaxで取得するメールクライアントを考えてみましょう。連絡先が変更されていない場合、Expiresを設定しておけばキャッシュから読み込まれます。URLに最終更新のタイムスタンプ(例:&t=1190241612)を付与することで、変更があったときだけ新しいデータを取得し、そうでなければブラウザキャッシュを使い回すことができます。

15. バッファを早めにフラッシュする

カテゴリ: サーバー

ユーザーがページをリクエストした際、サーバーがHTMLを組み立てるのに200〜500ミリ秒ほどかかり、その間ブラウザはデータが届くのを待っています。PHPのflush()関数などを使用すると、準備ができたHTMLの一部(HEAD部分など)を先に送信できます。これにより、ブラウザはバックエンドが残りの部分を処理している間に、CSSやJSなどのコンポーネントの読み込みを並行して開始できます。

理想的なフラッシュの位置はHEADの直後です。HEAD部分は生成が容易であり、CSSやJSが含まれているため、ブラウザはこれらを早期に取得し始めることができます。

      ... <!-- css, js -->
    </head>
    <?php flush(); ?>
    <body>
      ... <!-- content -->

Yahoo!検索ではこの技術をいち早く採用し、実際のテストでも多くの利点が証明されています。

16. AjaxにはGETリクエストを使用する

カテゴリ: サーバー

Yahoo!メールチームは、XMLHttpRequestを使用する際、ブラウザのPOSTリクエストが「ヘッダー送信」と「データ送信」��2ステップで行われることを発見しました。GETなら(Cookieが大量でない限り)1つのTCPパケットで済みます。ただしIEのURL長制限(2KB)には注意が必要です。

HTTPの仕様上、GETは情報の取得に使用されるべきであり、セマンティクスの面でもデータの取得にはGETを使用するのが正しい姿です。

17. コンポーネントの後回し読み込み(Lazy Load)

カテゴリ: コンテンツ

最初のレンダリングに本当に必要なものは何かを自問してください。それ以外は後回しにできます。

ドラッグ&ドロップやアニメーションのためのライブラリなどは、最初の表示には不要なことが多いため、onloadの後に読み込むのが理想的です。非表示のコンテンツや、スクロールしないと見えない画像なども後回しにできます。

YUI Image Loaderなどのツールも役立ちます。まずはページを正常に機能させ、その後に追加機能を後回し読み込みで強化するという「プログレッシブ・エンハンスメント」の考え方に従いましょう。

18. コンポーネントをプリロードする

カテゴリ: コンテンツ

プリロードは後回し読み込みとは逆のように見えますが、目的が異なります。ブラウザのアイドル時間を利用して将来必要になるコンポーネント(画像、スタイル、スクリプト)をあらかじめ取得しておくことで、次のページへ遷移した際に高速に読み込まれるようになります。

  • 無条件プリロード:Googleのトップページで検索結果ページ用のスプライト画像を先に取得しておくようなケース。
  • 条件的プリロード:ユーザーの入力に基づき、次に遷移するであろうページを予測して取得する。
  • 事前のプリロード:サイトのデザイン変更前にあらかじめ新しいリソースをキャッシュさせておき、リリース時の速度低下感を軽減する。

19. DOM要素の数を削減する

カテゴリ: コンテンツ

複雑なページは読み込みバイト数が増えるだけでなく、JavaScriptによる DOM アクセスも遅くなります。500個の要素を回すのと5000個を回すのでは大きな差があります。

大量のDOM要素は、マークアップに無駄がある兆候です。レイアウトのためにネストされたテーブルや余分な<div>を使いすぎていませんか?YUI CSS utilitiesなどを活用し、意味のあるセマンティックなマークアップを心がけましょう。Yahoo!のトップページでも要素数は700未満に抑えられています。

20. コンポーネントを複数のドメインに分散させる

カテゴリ: コンテンツ

ドメインを分けることで並列ダウンロード数を増やせますが、DNSルックアップのコストを考慮し、2〜4つのドメインに留めるのがベストです。例えば、HTMLはwww.example.orgに、静的ファイルはstatic1.example.orgstatic2.example.orgに分けるといった形です。

21. iframeの使用を最小限にする

カテゴリ: コンテンツ

iframeは別のHTMLドキュメントを埋め込むことができますが、動作を理解して効率的に使う必要があります。

メリット:サードパーティ製コンテンツ(広告など)の読み込み、セキュリティサンドボックス、スクリプトの並列ダウンロード。

デメリット:空白であってもコストが高く、親ページの読み込みをブロックし、セマンティックではない。

22. 404エラーをなくす

カテゴリ: コンテンツ

無意味なレスポンス(404など)のためにHTTPリクエストを消費するのは時間の無駄です。特に外部JSファイルへのリンク切れは、並列ダウンロードをブロックし、ブラウザがエラーページをJSとして解析しようとする無駄な試みを引き起こします。

23. Cookieのサイズを小さくする

カテゴリ: Cookie

Cookieは常にサーバーとブラウザ間でやり取りされます。不要なCookieを削除し、サイズを最小限に抑え、適切なドメインスコープと有効期限を設定して、ユーザーの応答時間への影響を最小化してください。

24. コンポーネントをCookieのないドメインから提供する

カテゴリ: Cookie

静的な画像リクエストにCookieを添えて送るのはネットワーク帯域の無駄です。静的ファイル専用のドメイン(例:static.example.org)を用意しましょう。ただし、メインドメインでCookieを設定しているとサブドメインにも送信されるため、Yahoo!のyimg.comのように、全く別のドメイン名を使用するのがより効果的です。

25. DOMアクセスを最小限にする

カテゴリ: JavaScript

JavaScriptによるDOMアクセスは非常に遅いです。アクセスの高速化のために以下を心がけましょう:

  • 一度アクセスした要素の参照をキャッシュする
  • ノードを更新する際は、DOMツリーに追加する前にメモリ上で(オフラインで)行う
  • JavaScriptでレイアウト崩れを直すような使い方は避ける

26. 賢いイベントハンドラを使用する

カテゴリ: JavaScript

多くのイベントハンドラをDOM要素に個別に付けると、応答性が低下します。イベント委譲(Event Delegation)を使用し、親コンテナで一括してイベントをキャッチしましょう。また、すべての画像の読み込みを待つonloadではなく、要素が利用可能になった時点で処理を開始できるDOMContentLoadedやYUIのonAvailableを活用しましょう。

27. @importよりも<link>を選択する

カテゴリ: CSS

IEにおいて@importを使用すると、ドキュメントの底部で<link>を使用するのと同様に段階的レンダリングを阻害します。CSSは常に上部でLINKタグを使って読み込むべきです。

28. フィルタの使用を避ける

カテゴリ: CSS

IE独自のAlphaImageLoaderフィルタはレンダリングをブロックし、メモリ消費を増大させます。可能な限りPNG8で代用するか、ハック(_filter)を使用して上位バージョンのIEユーザーに影響を与えないようにしましょう。

29. 画像を最適化する

カテゴリ: 画像

  • GIFパレットの調整。
  • GIFからPNGへの変換。アニメーションを除けばPNGの方が軽量で高性能です。
  • pngcrushなどのツールでPNGを最適化。
  • jpegtranでJPEGの不要なメタデータ(EXIFなど)を削除し、画質を落とさずに圧縮。

30. CSSスプライトを最適化する

カテゴリ: 画像

  • スプライト画像内では、縦よりも横に並める方がファイルサイズが小さくなる傾向があります。
  • 似た色を近くに配置し、色数を抑える(PNG8など)。
  • モバイル端末でのメモリ消費を抑えるため、不要な余白を減らす。

31. HTMLで画像をスケーリングしない

カテゴリ: 画像

HTMLで幅と高さを指定できるからといって、大きな画像を使わないでください。100x100pxで表示したいなら、元の画像も100x100pxであるべきです。500x500pxの画像を無理やり縮小させてはいけません。

32. 小さくキャッシュ可能なfavicon.icoを使用する

カテゴリ: 画像

favicon.icoは無視していてもブラウザが自動的にリクエストするため、404を返さないようにし、1KB以下に抑え、長期間キャッシュされるように設定してください。また、faviconのリクエストは他のコンポーネントのダウンロード順序を乱すことがあるので注意が必要です。

33. すべてのコンポーネントを25K未満にする

カテゴリ: モバイル

これはiPhoneのキャッシュ制限(非圧縮で25KB以上はキャッシュされない)に基づいたルールです。Gzip後のサイズではなく、元のサイズを小さく保つことが重要です。

34. コンポーネントを1つの複合ドキュメントにパックする

カテゴリ: モバイル

マルチパートドキュメントのように、複数のコンポーネントを1回のリクエストで取得する手法ですが、サポート状況に注意が必要です。

35. 画像のsrc属性を空にしない

カテゴリ: サーバー

src=""はブラウザによってドメイン直下やページそのものへの無駄なリクエストを発生させます。これはサーバー負荷を増大させ、ユーザーデータを汚染する可能性があるため、決して空にしてはいけません。

HTML5の仕様でも、src属性に空文字列を指定してはならないと明記されています。

この原則はJavaScriptの権威 Nicolas C. Zakas氏の記事に触発されたものです:Empty image src can destroy your site

コメント

コメントはまだありません

コメントを書く