Webサイトの性能向上については様々な方法があります。今後性能上の問題が発生した際にどのような選択肢があるかの参考用に備忘としてまとめています。
参考サイト
Best Practices for Speeding Up Your Web Site
※目次をクリックすると目次の下部にコンテンツが表示されます。
- 1.ブラウザの構成要素と処理の流れ
- 2.クリティカルレンダリングパスの最適化
- 3.WebサイトでHTTPリクエストの数を最小化するには?
- 4.コンテンツデリバリーネットワーク(CDN)、CloudFlare
- 5.ブラウザの並行ダウンロード数
- 6.Webのキャッシュの種類とその概要
- 7.Last-Modifiedヘッダとブラウザキャッシュ
- 8.ExpireとCache-Control: max-ageヘッダーを使ったブラウザキャッシュ
- 9.Cache-Controlヘッダーを使ってキャッシュポリシーを設定
- 10.301リダイレクトレスポンスのブラウザキャッシュ
- 11.DNSルックアップと並行ダウンロード
- 12.ブラウザによるDNSのキャッシュとDNSのプリフェッチ
- 13.PHPスクリプトで出力データを圧縮
- 14.バッファを早めにフラッシュする
- 15.JavaScriptのEvent Delegation
- 16.WebサーバーでGzip圧縮を使用
- 17.イメージマップを使ってHTTPリクエスト数を削減
- 18.CSS Spriteを使ってHTTPリクエスト数を削減
- 19.インライン画像を使ってHTTPリクエスト数を削減
- 20.スタイルシートを先頭に置いて性能向上
- 21.Scriptタグの位置が与える影響
- 22.Minification、ObfuscationでJavaScriptのサイズを小さくする
- 23.JavaScriptとCSSを外部ファイルにするかインラインにするか
- 24.ETagの仕組み、設定方法
- 25.画像の最適化
(1)ブラウザの構成要素
①UI(User Interface)レイヤー
・ブラウザのGUIなどのインタフェース。
・アドレスバー、リロードボタン、戻る、進むボタンなど
②ネットワークレイヤー
・TCP、HTTPコネクション。
・コンテンツをダウンロードし、レンダリングエンジンに渡す。
③レンダリングエンジン
・スクリーンにコンテンツを表示。
・Chrome、Safari、Operaで使用されているWebKitなど。
・JavaScriptが使用されている場合、JavaScriptインタプリタに渡す。
④JavaScriptエンジン
・JavaScriptをパース、実行。
・GoogleのV8など。
(2)処理の流れ
1)ユーザーがアドレスバーにURLを入力
・UIレイヤーがリクエストをネットワークレイヤーに渡す。
2)ネットワークレイヤーがネットワークを接続し、コンテンツをダウンロード
3)レンダリングエンジンが下記処理
・字句解析、パース、トークン化し、DOM要素に変換する。
・最初にDOM要素のレイアウトを行い、次にスクリーンに表示する。
・パース、トークン化中にscriptタグがあった場合、一時処理を中断し次の処理を判断する。
scriptタグが外部のJavaScriptファイルを指し示している場合、ネットワークレイヤーに依頼してダウンロードし、JavaScriptエンジンによって解析、実行する。
インラインのJavaScriptの場合は、JavaScriptエンジンによって解析、実行する。
JavaScriptの実行が完了するとレンダリングエンジンはパースを再開する。
・Webサイトにおけるエンドユーザーのレスポンス時間の80%はフロントエンド上で費やされる。
画像、スタイルシート、スクリプト、Flashなどのコンポーネントをダウンロードするのに大半の時間を割いている。
●ファイルを結合してHTTPリクエストの数を減らす
・すべてのスクリプトファイルを一つのスクリプトファイルにまとめるとHTTPリクエストの数を減らす事が出来る。すべてのCSSを一つのスタイルシートにまとめるのと同様の効果
・しかし、スクリプトやスタイルシートがページ毎に異なる場合はファイルを結合するのは難しい面もある。
・背景画像を一つの画像に結合したり、CSSの”background-image”と”background-position”属性をを使って必要な画像領域を表示する事もできる。
※参考CSS Sprites
・Image mapsを使うと複数の画像を一つの画像に結合できる。
全体のサイズはほぼ同じだが、HTTPリクエストの数を減らす事によってページの表示をスピードアップできる。
Image mapsは画像がページ内で隣接している場合(例えばナビゲーションバーなど)のみ使用できる。
・インライン画像は、実際のページ内に画像を組み込むのに”data: URL scheme”を使用する。
これは、HTMLドキュメントのサイズは増加させてしまう。
インライン画像をキャッシュされるスタイルシートに結合する事によってHTTPリクエストの数を減らす事が出来、ページサイズの増加を避けることも出来る。
ただし、インライン画像はブラウザによってはサポートされていないので注意が必要。
(1)コンテンツデリバリーネットワーク(CDN)の概要
・ユーザーとWebサーバーとの距離は、レスポンス時間に大きな影響を与える。
コンテンツを複数に広い範囲に地理的に分散させたサーバーにデプロイするとユーザーのページロード時間を速く出来る。
・地理的に分散したコンテンツを導入する方法の一つとして、Webアプリを分散構造に再設計する事があるが、デメリットもあり考慮が必要。
アプリの性質にもよるが、アプリの構造を変えるにはセッション状態の同期やデータベーストランザクションの複製などの手間のかかるタスクを含むため。
・エンドユーザーのレスポンス時間の80~90%はページ内の画像、スタイルシート、Flashなどのコンポーネントをダウンロードするのに費やされているという試算もある。
よってアプリの構造を再設計するという困難なタスクを開始するより、その静的コンテンツを分散して配置したほうが有効と言える。
これはコンテンツデリバリーネットワークを利用すれば容易に実現できる。
・大規模のインターネット会社は自前のCDNを所有しているが、CDNサービスプロバイダを使用するのがコスト面で有効。
CDNプロバイダには”Akamai Technologies”、”EdgeCast”、”level3″、”CloudFlare”などがある。
(2)CloudFlare
1)CloudFlareの概要
・CloudFlareのグローバルネットワークを介してキャッシュしたコンテンツを最適なルートを使って訪問者に配信。
・危険な攻撃をブロック、ボッドやクローラが帯域やサーバーリソースを浪費するのを制限。
・ドメインのDNSの設定を変更するのみで、ハードウェアを追加したりソフトウェアをインストールする必要はない。
・リアルタイムレポートやSSLを使用する場合は有料。
2)CloudFlareのCDNの概要
・JavaScript、CSS、画像などの静的リソースをCloudFlareがキャッシュし、ダイナミックコンテンツはWebサーバーが直接処理するように自動で行われる。
・サブドメイン単位に登録できる。
・Page Ruleを使ってページ単位に設定できる。
・トラフィックのサージがあってもCloudFlareのCDNがそのトラフィックを吸収する効果があって緩和してくれる。
3)CloudFlareのオプティマイザーの概要
●Rocket Loader、接続数を減少
・自動でWebページのネットワー接続数を最小化
・複数のJavaScriptファイルを一つのリクエストにまとめる。
●簡単にON/OFF、インストール不要
・多くのオプティマイザーサービスを簡単にON/OFFできる。
・利用するのに新たなハードウェアとソフトウェア、コードの変更が必要ない。
●AutoMinifi
・HTML、CSS、JavaScriptから不要な文字を除去してサイズを小さくする。
・キャッシュされない動的コンテンツでもこのサイズ減少によって恩恵を受ける。
●非同期でリソースをロード
・HTMLを最適化して、スクリプトのロードでレンダリングが出来るだけブロックされないようにする。
●ブラウザの最適化
・ブラウザの種類によって細かな差異があるが、サイトにアクセスするデバイスの種類に基づいて、外観に影響を与えないようにしつつ自動で最適化して性能を向上させる。
●Aggressive GZIP
●Local storage caching
・最新ブラウザやモバイルデバイスのローカルストレージを使用し、最も効率よくレンダリングできるようにオブジェクトをキャッシュ
4)CloudFlareのセキュリティ機能
CloudFareのセキュリティ機能は、コメントスパム、過剰なボットクロール、SQLインジェクション、DOS攻撃など幅広く対応する。
●自動で新しい攻撃を学習
・CloudFareネットワーク内のWebサイトに対する新しい攻撃を自動で検知。
ひとたびそれを新しい攻撃と認識するとそのWebサイトとネットワーク全体で攻撃をブロックする。
・CloudFareネットワーク内全体で学習した内容によって自分のサイトも保護されるのでセキュリティが高まる。
●攻撃のレポート
・ブロックした攻撃のリストを表示。
●ブラウザインテグリティチェック
・HTTPヘッダーのシグネチャを評価し、ブラウザのインテグリティをチェック。脅威があるリクエストの場合は拒否。
●Webレピュテーション
・すべてのビジターからの脅威データを使ってレピュテーションを形成。
・Webレピュテーションを使ってブロック。
※Webレピュテーションは、Webからの脅威の出所である不正URLへのアクセスをブロックするWebセキュリティソリューション。
●ブロックリスト、トラストリスト
・IPアドレス、IPアドレスの範囲ごとにブロックリスト、トラストリストを設定できる。
●SSH、Telnet、FTPポートを保護
・rootドメインでSSH、Telnet、FTPなどのポートを無効にし、保護出来る。
選択してサブドメインのみアクセス可能なように設定できる。
1)RFC2616の並行ダウンロード数
・RFC2616では、同一ホスト名からの並行ダウンロード数を最大2と記述。
Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.
2)最近のブラウザの並行ダウンロード数
・RFC2616が作られた時点ではレスポンスタイムとネットワークの輻輳を考慮して制限していたが、現状では問題ないと考えられ、各ブラウザで2以上の設定になっている。
IE10:8
Firefox4~:6
Opera10:8
Chrome4~:6
Safari3~:4
3)並行ダウンロード数を増加させるには?
・この制限はホスト名あたりで、サーバー、IPアドレス、ドメイン名あたりではない。
・同じサーバー内にあるリソースでも異なるサブドメインを使ってアクセスすればこの制限を受けない。
・並行ダウンロード数を増やしすぎるとWebサーバー側の問題で逆にレスポンスが長くなることもあるので注意する。
・2~4つのホスト名が妥当で4以上になると性能が劣化するとの調査結果がある。
・たくさんの小サイズのリソースの場合には並行ダウンロードは有効だが、大きなサイズの画像などの場合は当てはまらないので注意する。
4)プロキシーとHTTP1.0
・プロキシー経由でクライアントがWebサーバーにアクセスすると同一アドレスからのリクエストが増加する。
並行ダウンロード数が2で制限されている場合は問題となりづらいが、並行ダウンロード数が高い新しいブラウザだと同時アクセス数が増加して、IDSでSYNフラッドやDOSアタックと誤検知される恐れがある。
上記理由より最新のブラウザではプロキシー経由のアクセスの場合は並行ダウンロード数を低くしている。
・HTTP1.1をサポートしていないプロキシーも多く、この場合はHTTP1.0にダウングレードされる。
1)ブラウザによるキャッシュ
・直近や頻繁にアクセスしたリソースをクライアントのディスク領域に保存。
・どのリソースをキャッシュするかはサーバーによって指定されたポリシーに基づいて行う。
・ブラウザ毎にキャッシュの最大値が設定されている。
Firefox17:1024MB
IE9:ディスクスペースの1/256、最大250MB
Safari:制限無し
Opera10+:400MB
Chrome:300MB
・キャッシュ領域が満杯になったらLRUアルゴリズムで古いリソースを削除する。
削除する際、リソースの重要性は考慮されない。例えばJavaScriptのロードはDOMの構築をブロックしてWebページのロード時間が遅くなるので画像などのリソースと比べて性能上重要。
2)トランスペアレントプロキシーキャッシュ
・ISPや大規模な組織で、多くのクライアントがWeb通信する際にプロキシを介して行う。
・キャッシュによる性能上の効果とコンテンツフィルターなどセキュリティ面の効果もある。
3)リバースプロキシー
・Webサーバーの手前に設置し、キャッシュや負荷分散の役割を担う。
下記URLで画像ファイルをリクエストする場合を例にします。
http://example.com/image/bg.png
1)最初のリクエスト
①クライアントリクエスト送信
②サーバー応答
サーバーはリクエストされたbg.pngの最終更新時刻をLast-Modifiedヘッダーに記述してリターン。
HTTP/1.1 200 OK
Last-Modified: Tue, 21 Apr 2015 23:59:06 GMT
③ブラウザはbg.pngファイルをLast-Modifiedヘッダーで指定された時間の情報と一緒にキャッシュに保存。
2)2回目のリクエスト
①クライアントリクエスト送信
If-Modified-Sinceヘッダーに上記1)③で取得した日時を指定し、この時刻以降にファイルが更新されていたら画像ファイルを送信するように要求。
If-Modified-Since: Tue, 21 Apr 2015 23:59:06 GMT
②サーバー 304で応答
リクエストされたファイルが指定時刻以降に更新されていないので画像を送信せず、304ステータスで応答。
HTTP/1.1 304 Not Modified
●注意点
リクエストされたファイルのサイズが大きい場合はダウンロード時間が軽減され、キャッシュによる効果が大きいが、非常に小さいサイズのファイルの場合は、画像ファイルのサイズ分だけ軽減されるのみで、HTTPのリクエスト、応答自体は行われるので効果は少ない。
1)ExpireとCache-Control: max-ageヘッダーの概要
・リソースの有効期限を指定。その期間の間はクライアントはリクエストを送信しないで済む。(Last-Modifiedの場合はIf-Modified-Sinceリクエストを送信する必要がある。)
・ExpireはHTTP1.0にも対応している。
・Expireは特定の日時を指定する必要があり、サーバー/クライアント間での時刻の同期が必要で、期限が過ぎたら設定ファイルの日時も変更する必要がある。
一方、Cache-Control: max-ageヘッダーでは、期間を指定出来るので上記のような問題点は生じない。
・Cache-ControlはHTTP1.1のみ。
・両方指定されている場合は、Cache-Controlの設定が優先される。
2)Apacheのmod_expiresモジュール
・”ExpiresByType”を使ってExpiresヘッダーとCache-Control: max-ageヘッダーを設定できる。
例)期限を2ヵ月後に設定
ExpiresByType image/gif “access plus 2 months”
・画像、CSS、JavaScript、HTMLなどのリソースのタイプ毎にそれぞれ期限を設定できる。
3)期限内にリソースが更新された場合
期限内にリソースを更新した場合はクライアントにブラウザのキャッシュではなく、サーバーのリソースを使用するように認識させる必要がある。
下記のようにURLを変更して新規のリソースと認識させることによってサーバーにアクセスさせる。
①ファイル名に指定したバージョン、リビジョン番号、日付を変更
例)sample-1.0.1.js
②URLのパスに指定したバージョン、リビジョン番号、日付を変更
例)/javascript/2015-05-26/sample.js
③ドメイン名をDNSのCNAMEレコードを使ってURLを変更
例)http://cname101.example.com/javascript/sample.js
④URLにダミーの文字列を追加
例)example.js?v=1.0.1
※)一部のプロキシーサーバーではクエリー文字列を含むURLはキャッシュしない事があるので注意。
HTTP キャッシュの作成
(1)Apacheでの設定方法
Cache-Controlのmax-ageは、mod_expiresモジュールのExpiresByTypeディレクティブを使って設定しましたがmax-age以外はmod_headersモジュールのHeadersディレクティブを使って設定します。
例)
<FilesMatch “\.(php)$”>
Header set Cache-Control “private”
</FilesMatch>
(2)キャッシュポリシー設定に使用するCache-Controlヘッダーの主なオプション
※参考 RFC 2616 14.9 Cache-Control
●Cache-Control:max-age
・取得したレスポンスを再使用できる最大時間を、リクエストの時刻を起点とする秒数で指定。
・ブラウザのキャッシュやすべての中間キャッシュに格納できる(public)。
●Cache-Control:s-maxage
・クライアントのブラウザキャッシュには格納せず、中間キャッシュのみに格納。
●Cache-Control:public
・レスポンスに関連付けられたHTTP認証があり、さらにレスポンスのステータスコードが通常キャッシュ可能になっていない場合でも、そのレスポンスはキャッシュに格納できる。
ほとんどの場合、明示的なキャッシュ情報(”max-age”など)でレスポンスがキャッシュ可能であることが指定されているので、”public”は必要ない。
●Cache-Control:private
・ブラウザのキャッシュには格納できるが、中間キャッシュに格納することはできない。
たとえば、個人的なユーザー情報を含むHTMLページはそのユーザーのブラウザでのみキャッシュに格納でき、CDNでは格納できない。
●Cache-Control:no-store
・返されたレスポンスのバージョンにかかわらず、ブラウザのキャッシュやすべての中間キャッシュはそのレスポンスを一切格納できない。
たとえば、個人の機密データや銀行データが含まれているレスポンスなど。
・ユーザーがこのリソースをリクエストするたびに、リクエストがサーバーに送信され、毎回、完全なレスポンスがダウンロードされる。
●Cache-Control:no-cache
・レスポンスに変更があったかどうかをまずサーバーで確認してからでないとキャッシュを利用できない。
キャッシュ内のレスポンスを検証するためのラウンドトリップは発生するが、レスポンスに変更がなければリソースのダウンロードを省略できる。
(3)キャッシュポリシー設定例
パス タイプ キャッシュ
/page HTML Cache-Control:no-cache
/style.xxxx.css CSS Cache-Control:max-age=31536000
/script.xxxx.js JavaScript Cache-Control:private,max-age=31536000
/image.jpg 画像 Cache-Control:max-age=86400
①HTML
・「no-cache」が指定されているので、ブラウザはリクエストごとに必ずドキュメントを再検証し、コンテンツに変更がある場合に最新のバージョンを取得。
・CSS、JavaScriptファイル名にはフィンガープリントが埋め込まれているので、ファイルのコンテンツに変更があると、ファイル名が変更され、HTMLファイルの中身が更新される。
②CSS
・CSSはブラウザのキャッシュと中間キャッシュ(たとえば、CDN)への格納が認められ、有効期限が1年に設定。
・ファイル名にフィンガープリントが埋め込まれているのでCSSが更新されると、URLも変更される。
③JavaScript
・有効期限が1年に設定されているが、privateとし指定。これは、CDNがキャッシュに格納できない個人的なユーザーデータが含まれているため。
④画像
・有効期限が1日でファイル名にフィンガープリントを使用しない。
(1)以前の主要ブラウザの対応
・数年前までは主要ブラウザは、301リダイレクトレスポンスをキャッシュしていなかった。
例えば下記リダイレクト設定をしていた場合
http://example.com/old.html → http://example.com/new.html
過去にold.htmlへリクエストを送信し、301レスポンスを受け取っていたとしても、ブラウザのアドレスバーに”http://example.com/old.html”を入力するとnew.htmlへリクエストを送信せず、再度old.htmlへのリクエストを送信して301レスポンスを受け取ってしまう。
・301ステータスコードは、”Moved Permanently”で、恒久的移動を意味しているので、本来の意味ではこのレスポンスをキャッシュすべき。
(2)最近のブラウザの対応
・301リダイレクトレスポンスを期限を指定せず(No expiration time)キャッシュ。(Firefoxの場合は、about:cacheで確認できる)
無期限に設定されているので、キャッシュがFullになったり手動でキャッシュをクリアされないとずっとキャッシュに残り続ける事になる。
(3)Apacheで301リダイレクトレスポンスをキャッシュさせない設定
例)
RewriteRule ^/old.html$ http://example.com/new.html [R=301,L,E=nocache:1]
Header always set Expires “Thu, 01 Jan 1970 00:00:00 GMT” env=nocache
Header always set Cache-Control “no-store, no-cache, must-revalidate” env=nocache
①リライトルールがマッチした環境変数nocacheをセット。
②mod_headersモジュールは、環境変数nocacheがセットされている場合(RewriteRuleで301リダイレクトされた時)のみExpiresとCache-Controlヘッダーをセットする。
(4)「302 Found」の意味とブラウザキャッシュ
・発見した。リクエストしたリソースが一時的に移動されているときに返される。
・RFCでは、”Expires”、”Cache-control”ヘッダーでキャッシュを許可するように設定されている場合のみブラウザでキャッシュするようにと記述している。
(5)「303 See Other」の意味とブラウザキャッシュ
・他を参照せよ。リクエストに対するレスポンスが他のURLに存在するときに返される。
・RFCでは、このレスポンスはキャッシュに関するヘッダーの有無に関わらず、ブラウザにキャッシュしないようにと記述している。
(1)ブラウザによるDNSのキャッシュ
・クライアントではOSによってDNSのキャッシュが行われているが、ブラウザによってもDNSのキャッシュが行われる。
・各ブラウザによってキャッシュの仕様が異なる。
Firefoxの場合(about:configで確認)
network.dnsCacheEntries:40
network.dnsCacheExpiration:60
(2)DNSのプリフェッチ
1)DNSのプリフェッチの概要
・Webページ内にあるリンク内の外部ドメイン名の名前解決を事前に行っておく。
ユーザーが実際にリンクをクリックする際にすでに名前解決が完了しているので、読み込み時間の短縮を図ることができる。
2)DNSのプリフェッチの制御
①ブラウザの設定(Firefoxの場合)
※参考ページ
Controlling DNS prefetching – HTTP | MDN
・利用する場合は特に設定は必要ない。
・DNSのプリフェッチを無効にしたい場合
network.dns.disablePrefetchをtrueに設定する。
②サーバーの設定
・サーバー側でプリフェッチを無効にしたい場合は、HTTPヘッダーに以下を指定する。
X-DNS-Prefetch-Control: off
③リンクのURLがリダイレクトされる場合
・Googleの検索結果をリンクする場合やリダイレクトされる場合は、最終的にアクセスするホスト名をプリフェッチする事が出来ない。
そのような場合に、linkタグのrel=”dns-prefetch”を使って最終的にアクセスするホスト名をプリフェッチする事が出来る。
<link rel=”dns-prefetch” href=”//host_name_to_prefetch.com”>
※”dns-prefetch”の参考ページ
“DNS Prefetching – The Chromium Projects”
④Webページ内でプリフェッチの有効、無効を制御する
<meta http-equiv=”x-dns-prefetch-control” content=”off”>
1)サンプルコード
①Event Delegationを使用しない場合
<html ng-app="exampleApp"> <head> <script src="js/angular.min.js"></script> <link href="css/bootstrap.min.css" rel="stylesheet"> <script> angular.module("exampleApp", []) .controller("defaultCtrl", function ($scope) { $scope.changeBg = function (e) { switch (e.target.id){ case "primary": $scope.bgColor1 = "bg-primary"; break; case "success": $scope.bgColor2 = "bg-success"; break; case "info": $scope.bgColor3 = "bg-info"; break; } } }) </script> </head> <body ng-controller="defaultCtrl"> <div class="container"> <ul> <li id="primary" ng-click="changeBg($event)" ng-class="bgColor1">primary</li> <li id="success" ng-click="changeBg($event)" ng-class="bgColor2">success</li> <li id="info" ng-click="changeBg($event)" ng-class="bgColor3">info</li> </ul> </div> </body> </html>
②Event Delegationを使用した場合
<ul ng-click="changeBg($event)"> <li id="primary" ng-class="bgColor1">primary</li> <li id="success" ng-class="bgColor2">success</li> <li id="info" ng-class="bgColor3">info</li> </ul>
2)サンプルコードの説明
・①では、各リストにng-clickを記述していてコードのサイズを増大させている。
・②では、ng-clickをulタグに記述し、コードのサイズを減少させている。
・②では、JavaScriptの”event bubbling”の特性を利用している。
※”event bubbling”
ある要素でイベントがトリガーされるとそのイベントはイベントハンドラーに達するまで要素の祖先へとバブルアップしていく。
(1)概要
・Gzipは最もポピュラーで有効な圧縮方法の一つ。他には”defrate”もあるがあまり有効でなく、知られていない。
・Gzipによって約70%までサイズを減少でき、大半のブラウザがgzipをサポートしている。
・古いブラウザでは、ブラウザが要求している圧縮方式と実際に受信する圧縮コンテンツとの間のミスマッチが、ブラウザやプロキシーの間で起こる事がある。
Apacheモジュールは、自動で”Vary”ヘッダーを適切に設定する事によってサポートしている。
(2)HTTPプロトコルの圧縮の仕組み
HTTP1.1では、Webクライアントは”Accept-Encoding”ヘッダーで対応可能な圧縮方法を指定出来る。
①ブラウザが”Accept-Encoding”ヘッダーでクライアントのブラウザが対応可能な圧縮のタイプを指定
例)
Accept-Encoding: gzip, deflate
②サーバーは、”Content-Encoding”ヘッダーで圧縮に使用したタイプを通知
・Webサーバーは、クライアントが送ってきたリクエストのヘッダーを見て、そのリストにある圧縮方式で圧縮をし、下記のように実際に圧縮した方式を”Content-Encoding”ヘッダーに記載して送り返す。
例)
Content-Encoding: gzip
(3)どのタイプのコンテンツを圧縮するか?
・HTML、スクリプト、スタイルシート、XML、JSONは有効。
・画像、PDFファイルはすでに圧縮されているのでgzip圧縮するべきではない。
CPUリソースを無駄に使用するだけでなく、サイズが逆に増えてしまうこともある。
(4)どのような場合に圧縮を行うか?
・gzip圧縮する場合、サーバーが圧縮する際とクライアントが解凍する際に追加でCPUリソースを必要とするので、コンテンツのサイズ、ネットワーク帯域幅、クライアントとサーバーの距離などを考慮して圧縮を利用するか判断する。
・Apacheの場合、mod_gzip_minimum_file_sizeディレクティブで圧縮する場合の最小サイズを指定出来る。デフォルトは500バイト。
(5)Apacheでの設定方法
・Apache1.3では、mod_gzipモジュールを使用、Apache2.xでは、mod_deflateモジュールを使用。
●Apache2.xのmod_deflateモジュール
・mod_deflateという名前だが、gzipを使って圧縮する。
設定例)
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript application/javascript
DeflateCompressionLevel 9
<Directory “/your-server-root/manual”>
AddOutputFilterByType DEFLATE text/html
</Directory>
※DeflateCompressionLevel
・圧縮の程度を設定。大きな値では、より圧縮が行なわれるが、CPUリソースを消費。
・値は1(低圧縮)から9(高圧縮)まで。
●イメージマップを使用しない場合の例
一つの画像に一つのリンクを設定し、計4つの画像をダウンロードしています。
<a href=”nab1.html”><img border=0 src=”/images/nab1.gif”></a>
<a href=”nab2.html”><img border=0 src=”/images/nab2.gif”></a>
<a href=”nab3.html”><img border=0 src=”/images/nab3.gif”></a>
<a href=”nab4.html”><img border=0 src=”/images/nab4.gif”></a>
●イメージマップを使用した場合の例
一つの画像に4つのリンクを設定しているので、画像をダウンロードするHTTPリクエスト数を減らす事が出来ます。
<img usemap="#map1" border=0 src="/images/imagemap.gif"> <map name="map1"> <area shape="rect" coords="0,0,30,30" href="nab1.html"> <area shape="rect" coords="35,0,65,30" href="nab2.html"> <area shape="rect" coords="70,0,100,30" href="nab3.html"> <area shape="rect" coords="105,0,135,30" href="nab4.html"> </map>
(コードの説明)
①nab1.gif、nab2.gif、nab3.gif、nab4.gifを一つの画像にまとめたimagemap.gifを作成
②マップ全体を<MAP>タグで定義し、<AREA>タグでリンク領域とそのリンク先を指定。
③<IMG>タグでマップを適用する画像とその画像が使用するマップ名を指定。
(1)CSSスプライトとは?
・複数の画像を連結してひとつの画像ファイルにまとめ、CSSで表示範囲を指定する事によって個々の画像を表示する。
・複数の画像ファイルを一つにまとめる事によってHTTPリクエストの数を削減でき、表示の高速化を図れる。
・CSSの”background-image”プロパティを使って表示範囲を指定して個々の画像を表示する。
(2)CSS Spriteを使用しない場合の例
一つの画像に一つのリンクを設定し、計4つの画像をダウンロードしています。
<a href=”nab1.html”><img border=0 src=”/images/nab1.gif”></a>
<a href=”nab2.html”><img border=0 src=”/images/nab2.gif”></a>
<a href=”nab3.html”><img border=0 src=”/images/nab3.gif”></a>
<a href=”nab4.html”><img border=0 src=”/images/nab4.gif”></a>
(3)CSS Spriteを使用した場合の例
画像は一つのみで、4つのリンクに対して表示する部分を4分割して割り当てる事によって実現しています。
<style> #navbar span { display:inline; float:left; } .nab1 { width:30px;height:30px; background-image:url(/images/sprite.gif); background-position:0 0;; } .nab2 { width:30px;height:30px; background-image:url(/images/sprite.gif); background-position:-31px 0; } .nab3 { width:30px;height:30px; background-image:url(/images/sprite.gif); background-position:-62 0; } .nab4 { width:30px;height:30px; background-image:url(/images/sprite.gif); background-position:-93px 0; } </style> <div id="navbar"> <a href="nab1.html"><span class="nab1"></span></a> <a href="nab2.html"><span class="nab2"></span></a> <a href="nab3.html"><span class="nab3"></span></a> <a href="nab4.html"><span class="nab4"></span></a> </div>
①nab1.gif、nab2.gif、nab3.gif、nab4.gifを一つの画像にまとめたsprite.gifを作成
②ナビゲーションバーを表示する領域のCSSを設定
#navbar span { display:inline; float:left; }
③各メニューに対し、背景画像を表示する位置と範囲を設定
一つのボタンの幅が30pxで、表示位置を左に1ボタン分ずらすことによって各リンクごとに表示内容を変えています。
.nab2 { width:30px;height:30px; background-image:url(/images/sprite.gif); background-position:-31px 0; }
(4)CSSスプライト最適化の方法
・CSSスプライト内の個々の画像を垂直方法ではなく水平方向に並べるようにする。こうすると一般的にはサイズが小さくなる。
・CSSスプライト内の画像の色を類似した色にしてカラー数を少なくする。PNG8で扱えるように256色以下にするのが理想。
・モバイルフレンドリーとなるようにし、CSSスプライト内の画像の隙間を大きくしないようにする。
こうするとファイルサイズへの影響が少なくなるとともに画像をピクセルマップに伸張する際にユーザーエージョンとが必要とするメモリーが少なくなる。
(1)インライン画像の概要
・data:[<mediatype>][;base64],<data> 形式(data URI scheme)で画像データをエンコードしてWebページに埋め込む。
※data URI schemeについて
別途画像をHTTPリクエストによってダウンロードする必要が無くなり、HTTPリクエストを減らす事が出来る。
・バイナリデータをエンコードする事によってデータサイズは増加する。
・HTMLファイル内にインライン画像を使用する場合はキャッシュを利用できないので、複数のページに対象画像を使用している場合は、外部ファイルでキャッシュする効果と比較して導入を考慮する。
外部スタイルシートで背景画像と使用すればキャッシュを利用できる。
(2)使用例
1)HTMLファイル内で使用
<a href=”nab1.html”><img src=”data:image/gif;base64,R0lGODlh・・エンコードしたデータ”></a>
<a href=”nab2.html”><img src=”data:image/gif;base64,R0lGODlh・・エンコードしたデータ”></a>
※画像データをbase64でエンコード(Windowsのコマンドプロンプト)
certutil -encode i.gif o.txt
2)スタイルシートで使用
(外部スタイルシート)
.nab1 { background-image: url(data:image/gif;base64,R0lGODlh・・エンコードしたデータ);}
.nab2 { background-image: url(data:image/gif;base64,R0lGODlh・・エンコードしたデータ);}
(HTML)
<a href=”nab1.html”><span class=”nab1″></span></a>
<a href=”nab2.html”><span class=”nab2″></span></a>
1)スタイルシートを先頭に置く
・スタイルシートはDOMとともにレンダリングツリーを構成するので、headタグ内などファイルのトップに置き、出来るだけ早くインポートする。
・スタイルシートをHEADタグ内に置くとページがプログレッシブレンダリング(Javascritpなど処理の実行より、デザインなど、視覚的な部分をなるべく早く読み込ませ、表示を優先する事によって速い印象を与える)が行われる。
ユーザーに対してビジュアルに関わる部分、例えばプログレッシブインディケーター、ナビゲーションバー、ヘッダーロゴなど優先して表示させる事によって待ち時間を意識させないようにする。
2)スタイルシートをボトムに置いた場合の問題点
ボトムに置くとブラウザの種類やバージョンによって、プログレッシブレンダリングが阻害されたり、FOUC(the flash of unstyled content problem)が生じたりする。
3)サンプルコードで確認
●サンプルコード
(HTMLファイル) <!DOCTYPE html> <html> <head> <title>CSS Bottom</title> </head> <body> <div class=content> <p class="sample">CSS Top</p> <img src="sleep.php?sleep=4.1&type=gif"> </div> <link rel="stylesheet" href="sleep.php?sleep=1&type=css" type="text/css"> </body> </html> (sleep.php) <?php header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 60) . ' GMT'); if (isset($_GET["sleep"]) && is_numeric($_GET["sleep"])) { sleep($_GET["sleep"]); if (isset($_GET["type"]) && $_GET["type"] == "css" ) { header('Content-Type: text/css'); $contents = 'p.sample {background: #EEE; color: #606; font-weight: bold; padding: 10px;}'; } else { header('Content-Type: image/gif'); $filename = "spritebg.gif"; $handle = fopen($filename, "rb"); $contents = fread($handle, filesize($filename)); fclose($handle); } } print $contents; ?>
●動作内容
①プログレッシブレンダリングが行われずブランク画面が表示(Chromeの場合)
・スタイルシートのインポートが完了するまでブランク画面が表示。
・スタイルシートのインポート完了後に、スタイルが適用された状態で”CSS Top”の文字が表示。
②FOUC(the flash of unstyled content problem)の問題(IE11、FireFox)
・スタイルが適用されていない状態で”CSS Top”の文字が表示。
・スタイルシートのインポートが完了後”CSS Top”の文字にスタイルが適用されて再描画。
1)JavaScriptとDOM、CSSOM構築の影響
①DOM構築をブロック
・スクリプトは、ドキュメント内に挿入されたまさにその位置で実行される。
・HTMLパーサーがscriptタグに遭遇すると、DOM構築のプロセスを一時中断し、JavaScript エンジンに制御権を渡す。JavaScript エンジンの実行が完了すると、ブラウザは中断前の位置からDOM構築を再開。
②CSSOMをブロック
・ブラウザがCSSOMダウンロードと構築を完了していない場合は、完了するまで、スクリプトの実行を遅らせ、待っている間はDOM構築もブロックされる。
2)サンプルコードを使って検証
●サンプルコード
(HTML) <!DOCTYPE html> <html> <head> <title>Script Position</title> <link rel="stylesheet" href="sleep.php?sleep=1&type=css" type="text/css"> </head> <body> <div class=content> <p class="sample">Script Position</p> <span>Web Contents(Initial)</span><br /> <script src="sleep.php?sleep=5&type=js"></script> <img src="sleep.php?sleep=2&type=gif"> </div> </body> </html> (sleep.php) <?php header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 60) . ' GMT'); if (isset($_GET["sleep"]) && is_numeric($_GET["sleep"])) { sleep($_GET["sleep"]); if (isset($_GET["type"]) && $_GET["type"] == "css" ) { header('Content-Type: text/css'); $contents = 'p.sample {background: #EEE; color: #606; font-weight: bold; padding: 10px;}'; } elseif (isset($_GET["type"]) && $_GET["type"] == "js" ) { header('Content-Type: application/x-javascript'); $contents = "var span = document.getElementsByTagName('span')[0];". "span.textContent = 'Web Contents(Update)';". "span.style.color = 'red';". "var loadTime = document.createElement('div');". "loadTime.textContent = 'You loaded this page on: ' + new Date();". "loadTime.style.color = 'blue';". "document.body.appendChild(loadTime);"; } else { header('Content-Type: image/gif'); $filename = "spritebg.gif"; $handle = fopen($filename, "rb"); $contents = fread($handle, filesize($filename)); fclose($handle); } } print $contents; ?>
●検証結果
①scriptタグを中央に置いた場合
<span>Web Contents(Initial)</span><br />
<script src=”sleep.php?sleep=5&type=js”></script>
<img src=”sleep.php?sleep=2&type=gif”>
・”Web Contents(Initial)”が表示された後、JavaScriptの処理が完了するまでDOM構築がブロックされるので、その後の画像が表示されない。
②scriptタグをトップに置いた場合
<script src=”sleep.php?sleep=5&type=js”></script>
<span>Web Contents(Initial)</span><br />
<img src=”sleep.php?sleep=2&type=gif”>
・”TypeError: span is undefined”のエラー発生。
スクリプトはドキュメント内に挿入されたまさにその位置で実行される。このスクリプトでは、<span>タグのテキストを置換する処理を行っているが、<span>タグがscriptタグより下に記述されているのでエラーが発生。
③scriptタグをボトムに置いた場合
<span>Web Contents(Initial)</span><br />
<img src=”sleep.php?sleep=2&type=gif”>
<script src=”sleep.php?sleep=5&type=js”></script>
・scriptタグによってDOM構築がブロックされないので画像はすぐに表示される。
(1)JavaScriptのMinification(縮小化)
・改行コード、空白、タブなど不要な文字を削除する事によってファイルサイズを減らすとロード時間を改善する事が出来る。
・主なツール
JSMin
(2)JavaScriptのObfuscation
・Minificationと同様に改行コード、空白、タブなど不要な文字を削除。
・関数は変数の名前を短い文字列に変換。
・リリース前にソースコードを人間が読みにくく変換し、リバースエンジニアリングを困難にする目的もある。
・Minificationよりサイズの縮小効果は大きいが、処理が複雑でバグが発生する恐れがある。
・主なツール
ShrinkSafe
(3)CSSのMinification
・CSSは一般にJavaScriptと比べてコメントや空白が少ないのでJavaScriptと比べると効果が少ない。
(4)インラインのscript、style
外部ファイルのサイズ減少化だけでなく、インラインのscriptタグブロックやstyleタグブロックも不要なコードを削除してサイズを小さくするべき。
(5)gzip圧縮との併用
MinificationやObfuscationをgzip圧縮と併用して実施してもファイルサイズの削減効果はある。
・JavaScriptとCSSをHTML文書内にインラインで記述するとHTML文書がリクエストされるたびにダウンロードされることになる。この場合、HTTPリクエストの回数は少なくなるがHTML文書のサイズは増大する。
一方、外部ファイルにしてブラウザにキャッシュされた場合は、HTTPリクエストの数を増大させずにHTML文書のサイズを小さく出来る。
・どちらが有利かのキーポイントは、HTML文書のリクエスト回数と比較して、外部JavaScriptとCSSコンポーネントがどのくらいの頻度でキャッシュされるかによる。
・もしユーザーが1セッションあたりに複数のページビューが発生し、多くのページで同じ外部ファイルを再利用できる場合は、外部ファイルのキャッシュによって大きなメリットを受ける。
・インラインが有利になるのは、サイトのホームページなどで1セッションあたりに1ページビューのような場合が考えられる。
・多くのページビューの一番最初に表示されるようなフロントページの場合、外部ファイルによるキャッシュとインラインによるHTTPリクエストの減少とを平準化させるテクニックもある。
フロントページのJavaScriptとCSSはインラインにし、ページのロードが終了した時点で動的に外部ファイルをダウンロードする。それ以降のページはブラウザにキャッシュされた外部ファイルを参照することが出来る。
1)ETags(Entity tags)の仕組み
・ETagsはWebサーバーとブラウザが、ブラウザのキャッシュ内のコンポーネントがサーバー上のオリジナルと一致しているかどうか確認するのに使用する仕組み。
・ETagは、コンポーネントのある特定のバージョンを一意に識別できる文字列。文字列はクォートして指定する。
・サーバーとブラウザとの間の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を指定してサーバーに送り返す。
GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: “10c24bc-4ab-457e1c1f”
③ETagがサーバー上のコンポーネントと一致したら”304″ステータスコードをブラウザに送る。12195バイトのコンテンツそのものを送る必要がなくなる。
HTTP/1.1 304 Not Modified
・ETagの問題点は、サイトを提供しているサーバーでユニークとなるようにETagの値が決められているという事にあり、同じコンテンツでもクラスターなどで異なるサーバーからコンテンツが返されるとETagが異なっているため違うコンテンツとして認識させてしまう。
2)ApacheのETagの設定方法
・ApacheでのETagの値は、デフォルト設定ではファイルのinode,サイズ、最終修正時刻(mtime)から作成されるので、対象のファイルが複数のサーバーの同じディレクトリ内に存在していて、ファイルサイズ、パーミッション、タイムスタンプが同じだったとしてもinodeはサーバーが異なると違う値になってしまう。
・クラスター環境などでETagによる効果が認められない場合には、ETagの値を違う属性を使って生成したり、Etagフィールドを付加せず”Last-Modified”ヘッダーを使わない事を検討する。
ETagを付加しなければ、HTTPヘッダーのサイズがその分少なくなる。
①Etagの値を違う属性を使って生成する
INode、MTime、Sizeを組み合わせて設定できるので、MTimeとSizeのみ使用する場合は、下記のように設定する。
FileETag MTime Size
②ETagフィールドを応答に付加しない
FileETag none
下記サイトの資料を読み簡単にまとめました。
クリティカル レンダリング パス ? Web Fundamentals
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=ja
(1)クリティカルレンダリングパスとは
●クリティカルレンダリングパスとは?
HTML、CSS、JavaScriptのバイトの取得からピクセルとしてレンダリングする中間段階。
●クリティカルレンダリングパスの最適化
・ページのパフォーマンスを高める上で重要。
・ユーザーがページ上で行う中核的操作に関係するコンテンツを優先する必要がある。
・クリティカルレンダリングパスを最適化することで、最初にページがレンダリングされる時間を大幅に改善することができる。
・クリティカルレンダリングパスに対する理解は、優れたインタラクティブアプリケーションを構築する基盤にもなる。
(2)オブジェクトモデルを構築
●性能との関係
・ブラウザは、ページをレンダリングする前に、DOMツリーとCSSOMツリーを構築する必要があるので、HTMLとCSSの両方をできる限り早くブラウザに渡す必要がある。
●ドキュメントオブジェクトモデル(DOM)の構築
①受信データを文字に変換
ブラウザは、ディスクやネットワークからHTMLの未加工のバイトを読み取り、ファイルに指定されているエンコード方法(UTF-8など)に基づいて個々の文字に変換。
②トークン化
ブラウザは、文字列を<html>や<body>タグなど個々のトークンに変換。
③字句解析
発行されたトークンは、プロパティとルールを定義する「オブジェクト」に変換される
④DOM構築
HTMLマークアップで定義されているタグごとの関係性に基づき、ツリー型のデータ構造の中でリンクされる。
●CSSオブジェクトモデル(CSSOM)
・headセクションなどでlinkタグでCSSスタイルシートを参照している場合、このリソースに対してリクエストをディスパッチ。
・取得したCSSルールは、HTMLと同様、ブラウザが理解可能、処理可能なものに変換する必要があり、HTMLのケースと同様のプロセスが繰り返される。
(3)レンダーツリーの概要
CSSOMツリーとDOMツリー
↓
レンダーツリー作成
↓
レイアウト
↓
ペインティング
●レンダーツリーとは?
・CSSOMツリーとDOMツリーを組み合わせたもの。
このツリーを使って各表示要素のレイアウトを計算し、画面にピクセルをレンダリングするペインティング処理の入力としても使用される。
●レンダーツリーの作成
①DOMツリーのルートから順に、表示可能な各ノードを確認。
・スクリプトタグやメタタグなど表示されないノードは除外。
・「display: none」などCSSで非表示に設定されているノードも除外。
※「visibility: hidden」は、空のボックスとしてレンダリングされるので除外されない。
②表示可能なノードごとに、適切な一致する CSSOM ルールを見つけて適用。
③コンテンツと計算適用済みスタイルを持つ表示可能なノードを表示。
●レンダーツリー→レイアウト
レンダーツリーによって表示可能なノードとそのノードの計算適用済みのスタイルは分かったが、デバイスのビューポート内でのノードの正確な位置とサイズはまだわからない。
この判断を行うのが「レイアウト」(リフロー)段階。
●レイアウト→ペインティング、ラスタライジング
レンダーツリーの各ノードを画面上の実際のピクセルに変換。
●性能上の注意
・ドキュメントのサイズ、適用するスタイル、実行環境のデバイスによって必要な時間が異なる。
・ドキュメントが大きいほど、ブラウザで必要な処理が増える。
・スタイルが複雑なほど、ペインティングに要する時間が長くなる。
単色であればペインティングに「コストはかからない」が、ドロップシャドウになると計算とレンダリングにもっと多くの「コストがかかる」。
(4)CSSのレンダリングブロックとメディアタイプ、メディアクエリー
●レンダリングブロック
・DOMとCSSOMはレンダリングツリーの構成要素なのでDOMはもちろんのことCSSの準備が終了しないとレンダリングが開始されない。(CSSのレンダリングブロック)
・CSSはレンダリングをブロックするのでできる限り早くクライアントに渡し、最初のレンダリング時間を最適化する必要がある。
●CSSのレンダリングブロックとメディアタイプ、メディアクエリー
・メディアタイプやメディアクエリを利用すると、一部のCSSリソースを非レンダリングブロックとしてマーキングすることができる。
例)ページを印刷する場合のメディアタイプ
<link href=”print.css” rel=”stylesheet” media=”print”>
ページが印刷される場合にのみ適用されるので、ページが最初にブラウザに読み込まれる際にはレンダリングブロックとはならない。
例)メディアクエリを指定した例
<link href=”other.css” rel=”stylesheet” media=”(min-width: 40em)”>
メディアクエリが指定した条件に合致すると、ブラウザは、スタイルシートのダウンロードと処理が完了するまでレンダリングをブロック。
・CSS リソースは、ブロック リソースであるか非ブロックリソースであるかにかかわらず、すべてブラウザによってダウンロードされる。
(5)CSSのレンダリングブロックとメディアタイプ、メディアクエリー
●レンダリングブロック
・DOMとCSSOMはレンダリングツリーの構成要素なのでDOMはもちろんのことCSSの準備が終了しないとレンダリングが開始されない。(CSSのレンダリングブロック)
・CSSはレンダリングをブロックするのでできる限り早くクライアントに渡し、最初のレンダリング時間を最適化する必要がある。
●CSSのレンダリングブロックとメディアタイプ、メディアクエリー
・メディアタイプやメディアクエリを利用すると、一部のCSSリソースを非レンダリングブロックとしてマーキングすることができる。
例)ページを印刷する場合のメディアタイプ
<link href=”print.css” rel=”stylesheet” media=”print”>
ページが印刷される場合にのみ適用されるので、ページが最初にブラウザに読み込まれる際にはレンダリングブロックとはならない。
例)メディアクエリを指定した例
<link href=”other.css” rel=”stylesheet” media=”(min-width: 40em)”>
メディアクエリが指定した条件に合致すると、ブラウザは、スタイルシートのダウンロードと処理が完了するまでレンダリングをブロック。
・CSS リソースは、ブロック リソースであるか非ブロックリソースであるかにかかわらず、すべてブラウザによってダウンロードされる。
参考サイト
画像の最適化 | Web Fundamentals – Google Developers
1)画像を使用せず他の方法で代替
①CSS効果(グラデーション、シャドウなど)やCSSアニメーション
・解像度やズームのレベルにかかわらず、常に鮮明に表示される解像度に依存しないアセットを画像ファイルに必要な容量の何分の1かで生成できる。
②ウェブフォント
・美しい字体を利用できるだけでなく、テキストの選択、検索、サイズ変更の機能もそのまま利用できるため、操作性が大幅に向上。
2)ベクター画像とラスター画像
①ベクター画像
・線、点、ポリゴンを使って画像を表す。
・単純な幾何学的図形(ロゴ、テキスト、アイコンなど)で構成される画像に最適。
・解像度とズームの設定にかかわらず鮮明に結果が出力されるため、高解像度画面やさまざまなサイズで表示する必要があるアセットに理想的な形式。
・写真などシーンが複雑な場合には適していない。
すべての形状を表すSVGマークアップの量が極めて多くなることがあり、「写真のようにリアルに」出力されない可能性がある。
このような場合は、GIF、PNG、JPEGなどのラスター画像形式や、JPEG-XR、WebPなどの最新の形式のいずれかを使用する。
②ラスター形式
・長方形のグリッド内の各ピクセルの値を個々にエンコードすることで画像を表す。
・ピクセルからなる単なる2次元グリッド。たとえば、100×100のピクセル画像は10,000個のピクセルが並んだもの。
・解像度やズームに依存しないといった同様の便利な特性はない。
・拡大すると、ギザギザしてぼやけた表示になる。
そのため、さまざまな解像度の複数のバージョンのラスター画像を用意する事が必要になる可能性がある。
3)高解像度画面
①CSSピクセルとデバイスピクセル
・1つのCSSピクセルに複数のデバイスピクセルが含まれていることがあり、デバイスピクセルの数が多いほど、画面上の表示コンテンツの精度は高くなる。
②高DPI(HiDPI)画面
・高解像度画面では1つのCSSピクセルに複数のデバイスピクセルが使用される。
・高解像度画像では、ピクセルの数とバイトの数を大幅に増加する必要がある。
・ベクター画像は解像度にかかわらず鮮明にレンダリングできるため、この処理に最適。
・ラスター画像はピクセル単位で画像をエンコードするため、ピクセルの数が多いほど画像のファイルサイズも大きくなる
4)ベクター画像の最適化
①SVG(Scalable Vector Graphics)
・2次元グラフィック向けのXMLベースの画像形式。
・最新のブラウザはすべてSVGをサポートしている。
・SVGマークアップはページに直接埋め込むことも外部リソースとして使用することもできる。
②SVGファイルの圧縮
・レイヤ情報、コメント、XML名前空間など、ブラウザでのアセットのレンダリングには必要ない多くのメタデータが含まれていて、これらはsvgoのようなツールを使って縮小化する事が出来る。
・GZIP圧縮が適用できるのでサーバーがSVGアセットを圧縮するように設定されていることを確認する。
5)ラスター画像の最適化
①ラスター画像のサイズ
・各ピクセルには”RGBA”の4チャンネル(Rは赤、Gは緑、Bは青、Aは透明性)の値が保存されている。
内部処理で、ブラウザはチャンネルごとに256の値(シェード)を割り当て、チャンネルあたり8ビット(28 = 256)、ピクセルあたり4バイト(4チャンネル x 8ビット = 32ビット = 4バイト)に変換される。
そのため、グリッドの寸法がわかれば、ファイルサイズを簡単に計算できる。
②カラーパレットを縮小して圧縮
・1チャンネル8ビットなのでR、G、Bの3つのチャンネルでは24ビットで16,777,216色になる。
カラーパレットを256色に減らすとR、G、Bの3つのチャンネルで8ビットとなり、2バイト削減できる。元のピクセルあたり4バイトの形式から50%圧縮できる。
③デルタエンコード
・ピクセルごとに値を保存するのではなく、隣接するピクセルとの違いを保存。
6)各画像形式の特徴
①GIF
・アニメーションが利用可能。
・カラーパレットが最大で256色に制限されるので色の選択肢を抑える事が出来る。
②PNG
・カラーパレットのサイズ選択以外は、ロスのある圧縮アルゴリズムは適用されない。
・最高画質の画像が出力されるが、他の形式よりもファイルサイズが著しく大きくなる。
③JPEG
・ロスのある最適化とロスのない最適化を組み合わせて画像セットのファイルサイズを削減。
④ベクター(SVG)形式
・画像アセットに幾何学的図形で構成された画像が含まれている場合。
⑤WebP
・平均で、同等のJPEG画像よりファイルサイズを30%縮小できる。
7)表示サイズに合わせてサイズを変更した画像を配信
・表示サイズに合わせて画像のサイズを変更することで不要なピクセルの数を削減する。
・画像の実サイズが表示サイズより大きいとブラウザが画像を縮小し、余分なCPUリソースが必要になる。
ブラウザはこのDNSルックアップが完了するまでこのホスト名のマシンからダウンロードする事が出来ない。
・DNSルックアップはレスポンスを向上させるためにキャッシュされる。
このキャッシュは、ユーザーが使用しているISPによって管理されているキャッシュサーバーなどによって行われるが、各ユーザーのPC内でもキャッシュされる。これは使用しているOSのDNSキャッシュを使って行われる。
そしてこのOS上のキャッシュ以外にブラウザによってもキャッシュされる。ブラウザによってDNSレコードが維持されている限り、OSによってDNSレコードをリクエストする必要はない。
・クライアントのDNSキャッシュ(ブラウザとOSの両方)が空の場合、各ページ、画像、スクリプトファイル、スタイルシート、Flashなどのソースが異なるホスト名に存在するとそのホストの数だけDNSルックアップをする事になる。
逆を言えば、ユニークなホスト名を減少させるとDNSルックアップの数も減らす事が出来る。
・ただし、ユニークなホスト名が減少するとページ内で発生する並行ダウンロードも減少する可能性がある。並行ダウンロードによってレスポンスタイムが節約できていた場合は、すべて同じホスト名にすると却ってレスポンスタイムが悪化する可能性もある。
ホスト名を2~4に分散させるぐらいがちょうど良いという意見がある。
この時間の間、ブラウザはデータが届くのを待ってアイドル状態になってしまう。
・PHPではflush()関数があり、この関数を使うと部分的に準備できているHTMLレスポンスをブラウザに送る事が出来、バックエンドがbusyでもブラウザはコンポーネントのfetch処理を始める事が出来る。
flush関数を使うメリットはbusyなバックエンドサーバの環境、または軽いフロントエンドの環境で享受できる。
・flush関数を使用する良い場所としては、HEADタグの直後がある。
理由は、生成するのが容易でCSSやJavaScriptファイルをインクルード出来、バックエンドがまだ処理中の間でも並行してブラウザがfetchを開始できる。
例)
<!– css, js –>
</head>
<?php flush(); ?>
<body>
<!– content –>