blog

ケーススタディ:ブラウザのキャッシュ・メカニズムを深く理解する

ブラウザのキャッシュはフロントエンドのパフォーマンス最適化の重要な部分であり、フロントエンドの効率向上のための重要性は自明です。 ブラウザのキャッシュについても半ば理解し、今回は東風のH5ページキャッ...

Sep 10, 2020 · 8 min. read
シェア
キャッシュは、ネットワークIO消費を削減し、アクセス速度を向上させることができます。ブラウザのキャッシュは、フロントエンドのパフォーマンスを最適化するシンプルで効果的な手段です。

序文

ブラウザのキャッシュは、フロントエンドのパフォーマンス最適化の重要な部分であり、フロントエンドの効率改善におけるその重要性は、いくら強調してもし過ぎることはありません。

ブラウザキャッシュ

ブラウザキャッシュ

キャッシュは、クライアント側、サーバー側を問わず、リモート・リソースをローカルに保存するメカニズムです。 同じURLを使用したデータ・リクエストは、ソース・サーバーにアクセスすることなく、キャッシュから直接リソースをリクエストすることができます。

ウェブフロントエンドキャッシングは、データベースキャッシング、サーバーサイドキャッシング、ブラウザキャッシングに大別されます。

ブラウザのキャッシュには、HTTPキャッシュ、indexDB、クッキー、ローカルストレージなど、様々なものがあります。ここでは、HTTPキャッシュに関連する内容のみを説明します。

ブラウザのキャッシュの意義:

ブラウザは、ユーザーが同じページを再度訪れたときに、ローカルディスクから直接ファイルをロードできるように、ユーザーが最近リクエストしたドキュメントをローカルに保存します。ブラウザのキャッシュの重要性は主に以下の点にあります:

a. 冗長なデータ転送を回避し、トラフィックを節約します;

b. ユーザーによるウェブページへのアクセスの高速化;

c. サーバーへのプレッシャーの軽減

キャッシュタイプ

最初のデータ要求

ブラウザが初めてデータを要求する時、ブラウザのキャッシュに対応するキャッシュデータがありません。この時、サーバーに要求する必要があり、ブラウザがデータを返した後、要求されたデータをキャッシュデータベースに保存します。

ブラウザにキャッシュされたデータが存在すると、リクエストをサーバに送る必要があるかどうかによって、キャッシュの種類は必須キャッシュとネゴシエーションキャッシュに分類されます。

強制キャッシュ

ユーザがデータをリクエストし、それが強力なキャッシュにヒットした場合、サーバからリクエストする代わりに、ローカルリソースから直接データを取得し、ディスクキャッシュまたはメモリキャッシュからのプロンプトとともに200ステータスコードを返します。

交渉キャッシュ

ユーザーがリソースを要求すると、ブラウザはリクエストを直接サーバーに送信し、サーバーとローカルリソースの比較をネゴシエートし、ローカルリソースが無効かどうかを検証します。

強制キャッシュとnegotiatedキャッシュの関係

強制キャッシュもネゴシエートされたキャッシュも、キャッシュされたリソースをヒットした後にローカル領域からリソースを読み込みます。強制キャッシュが有効な場合、サーバへの更なるリクエストは必要ありません。一方、ネゴシエートされたキャッシュは、キャッシュの使用の有無に関わらず、ネゴシエートするためにサーバにリクエストを送る必要があります。

両方のタイプのキャッシュ・ルールが同時に存在することができ、必須キャッシュはネゴシエートされたキャッシュよりも優先順位が高い、つまり、必須キャッシュのルールを実行するとき、キャッシュが有効であれば、キャッシュが直接使用され、ネゴシエートされたキャッシュのルールはもはや実行されません。必須キャッシュのルールが有効でない場合は、ネゴシエートされたキャッシュの判定が必要です。

キャッシュ関連ヘッダー

以上、強制キャッシュとネゴシエーション・キャッシュの処理について説明しましたが、ではブラウザでは、キャッシュされたデータが無効かどうかをどのように判断しているのでしょうか?キャッシュされたデータを使用するかどうかを確認する方法は?

強制キャッシュ

強制キャッシュのレスポンスヘッダには、期限切れルールを示す2つのフィールドがあります。

1.Expires:Expiresの値は、サーバーによって返される有効期限、つまり、次のリクエストは、要求時間は、キャッシュされたデータの直接使用、サーバーによって返される有効期限よりも短いです。しかし、ExpiresはHTTP 1.0のものであり、現在デフォルトのブラウザはHTTP 1.1をデフォルトとしているため、その役割は基本的に無視されます。もう一つの問題は、有効期限はサーバによって生成されますが、クライアントの時刻とサーバの時刻に誤差が生じる可能性があり、キャッシュヒットのエラーにつながることです。 そのため、HTTP 1.1 では、代わりに Cache-Control を使ってください。

a. 類似点:どちらも強力にキャッシュされます。

b. 違い:

  • Expiresはhttp 1.0で義務付けられており、Cache-Controlはhttp 1.1で義務付けられています。
  • Expiresの有効期限は絶対時間であるためエラーが発生しやすく、Cache-Controlの有効期限は相対時間であるためキャッシュに問題がありません。
  • 両方がリクエストに存在することは可能ですが、リクエストの中で一緒に動作することはありません。 HTTP 1.0 では、Cache-Control は機能せず Expires が機能します。HTTP 1.1 では、Expires は機能せず Cache-Control が機能します。現在の状況は一般的にhttp1.1なので、Expiresは後方互換性の一形態として存在します。
  • Cache-Controlはより多くのオプションがあり、より強力であり、推奨されます。 Expires は強力なキャッシュですが、単一の機能であり、推奨されません。

たとえば、次の図では、ファイルglobal.jsのCache-Controlでキャッシュの有効期限max-ageを86400sに指定しています:

交渉キャッシュ

ネゴシエーションによるキャッシュは一般的に、ブラウザによってキャッシュされたリソースが利用可能かどうかをサーバが判断するために、if-modified-since/Last-Modifiedとif-none-match/Etagを使います。

Last-Modified: サーバがリクエストに応答するとき、リソースが最後に変更されたのはいつかをブラウザに伝えます。

If-Modified-Since: ブラウザがリソースを再リクエストするとき、ブラウザはリクエストが行われたことと、返されたリソースの最終更新時刻をサーバーに通知します。

最終更新時刻がIf-Modified-Since以下の場合、レスポンスヘッダは304を返し、ブラウザに保存されたキャッシュの使用を継続するよう通知します;

Etag:サーバーがリクエストに応答する際、ブラウザに現在のリソースの一意な識別子を伝えます。

If-None-Match: サーバに再リクエストするとき、このフィールドを使ってクライアントのキャッシュデータの一意な識別子をサーバに知らせます。サーバはリクエストを受け取り、If-None-Match を見つけると、リクエストされたリソースの一意な識別子と比較します。もし異なっていれば、リソースが再び変更されたことを意味するので、リソースの内容全体に応答し、ステータスコード 200 を返します。もし同じであれば、リソースに新しい変更がないことを意味するので、HTTP 304 を応答し、ブラウザに保存されたキャッシュを使い続けるように通知します。

  1. もしリソースが1秒以内に複数回変更された場合、Etagはそれを判断して最新のリソースを返すことができます。
  2. パフォーマンスの面では、Last-Modifiedの方がEtagよりも優れています。Last-Modifiedは記録時間しか必要としませんが、Etagはサーバーがハッシュ値を再生成する必要があるため、パフォーマンスが若干悪くなります。
  3. 優先度の点では、Etag は Last-Modified よりも優れており、Etag と Last-Modified は同時に存在することができます。ローカルキャッシュの有効期限が切れた後、ブラウザはサーバにリクエストメッセージを送信します。リクエストヘッダにはIf-none-matchとLast-Modified-Sinceが含まれており、ローカルキャッシュのデータ検証がサーバと一致しているかどうかを確認するために使用されます。サーバ側では、Etag の判定を優先し、同じ場合は 304 を返し、異なる場合は Last-Modified の比較を続け、新しいリソースを返すかどうかを決定します。サーバ側がローカルキャッシュとサーバ側の整合性を確認し、304を返した場合、ブラウザはローカルキャッシュを読み込みます。そうでない場合、サーバは新しいEtagとLast-Modifiedの時間を与えながら、要求されたリソースを返します。

キャッシュ要求

以下は、ブラウザのキャッシュのプロセスです:

分析例

クライアント側にとっては、ローカルにキャッシュされたデータを利用する際に、ブラウザがローカルとサーバーのリソースをアライメントする必要がありますが、サーバー側にとっては、サーバーからクライアントにリソースが降りてくるため、サーバー側ではアライメントの制御ができなくなります。例えば、サーバ側ではキャッシュの有効期限にmax-ageを設定しており、この間にサーバ側のリソースが変更されても、サーバ側ではクライアント側にリソースの更新通知を通知することができません。そのため、ウェブページでは、ページの読み込み速度の向上とページの正確性の確保を両立させるために、キャッシュの非推奨と更新に対する合理的な応答戦略を指定する必要があります。

以下では、Huawei Cloud公式ウェブサイトのコンポーネントのコンテキストにおけるキャッシュの廃止と更新の対応戦略を分析します:

公式ホームページ

注釈

  • Html:キャッシュの有効時間を0秒にし、ページがロードされたときに、ブラウザが毎回ソースサーバーにデータを確認するようにします;
  • Css: 変更の頻度が低く、ローカルキャッシュが許可され、強制的なキャッシュ時間、強制的なキャッシュの有効期限、そしてネゴシエーションによるキャッシュがあります;
  • Js: ローカルキャッシュが許可され、強制キャッシュ時間、強制キャッシュ期限切れ、そしてネゴシエーションによるキャッシュがあります;
  • イメージ: イメージはあまり頻繁に変更されず、ローカルキャッシュが許可され、必須のキャッシュ時間があります;
  • Gif:公式サイト内のGifは主にバナーローテーションに存在するため、タイムリー性を確保するためにno-cacheを使用し、キャッシュを許可せず、毎回強制的にソースサーバーにデータを確認します。

上の図は、キャッシュ可能なファイルに対するキャッシュ戦略を説明したものです。しかし、ウェブページにはglobal.js、global.cssなど、更新頻度が高く、ローカルキャッシュを常時使用するとページの正しさに影響を与える可能性があるファイルが他にもたくさんあります。したがって、ファイルのこの部分を参照する場合、ローカルリソースの適時性を保証するためにキャッシュをリフレッシュするためにバージョン番号がファイルに追加されます。バージョン番号を追加する目的は、ファイルが読み込まれるたびにサーバーに再要求することを強制することです。次の左の図は、いくつかのファイルのバージョン番号の接尾辞です。ファイルのこの部分がブラウザで再読み込みされた後、リクエストメッセージのヘッダ、Request HeaderのCache-controlの値がno-cache、つまりキャッシュなしになり、データが再リクエストされます。下の右図のように

コミュニティ

注釈

  • Html:キャッシュのパーミッションは公開されています。ローカルキャッシュの有効期限は固定された Thu, 19 Nov 1981 08:52:00 GMT で、これはローカルキャッシュが常に期限切れであることを意味します。したがって、ページがロードされるたびに、ソースサーバーからリソースを再度フェッチする必要があります。
  • Css: 変更頻度が低く、ローカルキャッシュが許可され、強制キャッシュ時間は1日です;
  • Js: ローカルキャッシュを許可し、1日間のキャッシュを強制します;
  • イメージ:イメージの変更頻度が低く、png形式のファイルは、ローカルキャッシュを使用することが許可され、強制キャッシュ時間は1週間、1月のjpg形式のファイルです。ローカルキャッシュの有効期限が切れた後、それはEtagとLast-Modifiedを決定することにより、ローカルキャッシュの有効性を検証し続けます。ページ内の一般的なイメージリソースが大きいため、イメージのキャッシュ戦略は、同時に強力なキャッシュとネゴシエートされたキャッシュが、変更の頻度が低いので、キャッシュの使用は、ブラウザの読み込み速度を向上させることができます。

クラウドマーケットプレイス

注釈

  • Css: 1日間の強制キャッシュ; 強制キャッシュの有効期限切れとその後のネゴシエーションによるキャッシュ;
  • Js:1日キャッシュを強制します。キャッシュをネゴシエートする前に、強制的にキャッシュを期限切れにします;
  • イメージ:1週間の強制キャッシュ、強制キャッシュ期限切れ、その後ネゴシエーションによるキャッシュ;

パーソナルセンター

注釈

  • Css: 1日間の強制キャッシュ; 強制キャッシュの有効期限切れとその後のネゴシエーションによるキャッシュ;
  • Js: 1日または1週間のキャッシュを強制します;
  • イメージ:1週間の強制キャッシュ、強制キャッシュ期限切れ、その後ネゴシエーションによるキャッシュ;

フォーラム

注釈

  • Css: 1週間の強制キャッシュ、強制キャッシュ期限切れ、その後ネゴシエーションによるキャッシュ;
  • Js: 強制キャッシュは1週間、ファイルによって異なります;
  • イメージ:1週間の強制キャッシュ、強制キャッシュ期限切れ、その後ネゴシエーションによるキャッシュ;

App

概要

現在のWebページでは、css、js、imageなどの異なるタイプのファイルに対するキャッシュポリシーは多かれ少なかれ同じです。つまり、ストロングキャッシュとネゴシエートキャッシュの両方の戦略があります。ネゴシエートされたキャッシュでは、Last-Modified と Etag 識別子が与えられ、サーバサイドはクライアントサイドのキャッシュの有効性を検証します。この章では、公式ウェブサイトの各コンポーネントのブラウザ側のキャッシュ戦略を簡単に紹介しました。しかし、いくつかのファイルは特別なキャッシュ設定を持ちます。例えば、ページ内のjs、css、イメージなどの多くには、バージョン番号が付加されていたり、強制的にキャッシュをリフレッシュするなどの設定があります。





Read next

メモリへのデータ格納とディープコピーのネイティブJS実装

実行処理では、主に3種類のメモリ空間があります。それぞれ、コード空間、スタック空間、ヒープ空間です。コード空間は主に実行コードを格納するためのもので、スタックとヒープはデータを格納するためのものです。 このうち、プリミティブ型のデータ値は「スタック」に直接格納され、参照型の値は「ヒープ」に格納されます。一般的に、スタック領域はあまり大きく設定されず、主に...

Sep 10, 2020 · 3 min read