あらゆる場所で高速なサイトを開発するのは、一筋縄ではいきません。デバイスの機能の多様性と、デバイスが接続するネットワークの品質を考えると、この課題は克服できないように思えるかもしれません。ブラウザの機能を利用して読み込みのパフォーマンスを向上させることはできますが、ユーザーのデバイスの機能やネットワーク接続の品質はどうすれば確認できますか?その解決策は、Client Hints です。
クライアント ヒントは、ユーザーのデバイスと接続先のネットワークに関するこれらの側面に関する分析情報を提供する、オプトイン HTTP リクエスト ヘッダーのセットです。サーバーサイドでこの情報を利用することで、デバイスやネットワークの状態に基づいてコンテンツの配信方法を変更できます。これにより、より包括的なユーザー エクスペリエンスを実現できます。
コンテンツの交渉がすべて
クライアント ヒントは、コンテンツ ネゴシエーションの別の方法です。つまり、ブラウザのリクエスト ヘッダーに基づいてコンテンツ レスポンスを変更します。
コンテンツ ネゴシエーションの例として、Accept
リクエスト ヘッダーがあります。ブラウザが認識するコンテンツ タイプを記述します。サーバーは、この情報をレスポンスのネゴシエーションに使用できます。画像リクエストの場合、Chrome の Accept
ヘッダーの内容は次のとおりです。
Accept: image/webp,image/apng,image/*,*/*;q=0.8
すべてのブラウザは JPEG、PNG、GIF などの画像形式をサポートしていますが、この場合の Accept は、ブラウザが WebP と APNG もサポートしていることを意味します。この情報を使用して、ブラウザごとに最適な画像タイプをネゴシエートできます。
<?php
// Check Accept for an "image/webp" substring.
$webp = stristr($_SERVER["HTTP_ACCEPT"], "image/webp") !== false ? true : false;
// Set the image URL based on the browser's WebP support status.
$imageFile = $webp ? "whats-up.webp" : "whats-up.jpg";
?>
<img src="<?php echo($imageFile); ?>" alt="I'm an image!">
クライアント ヒントは Accept
と同様に、コンテンツを交渉するためのもう一つの手段ですが、デバイスの機能とネットワーク条件のコンテキストで使用します。クライアント ヒントを使用すると、ネットワーク状態が悪いユーザーにクリティカルでないリソースを提供するかどうかなど、ユーザーの個々のエクスペリエンスに基づいてサーバーサイドのパフォーマンスに関する決定を行うことができます。このガイドでは、使用可能なヒントと、それらを使用してコンテンツ配信をユーザーにとってより快適にするための方法について説明します。
オプトイン
Accept
ヘッダーとは異なり、クライアント ヒントは魔法のように現れるわけではありません(後述する Save-Data
を除く)。リクエスト ヘッダーを最小限に抑えるため、ユーザーがリソースをリクエストしたときに Accept-CH
ヘッダーを送信して、受信するクライアント ヒントを選択する必要があります。
Accept-CH: Viewport-Width, Downlink
Accept-CH
の値は、サイトが後続のリソース リクエストの結果の決定に使用するリクエストされたヒントのカンマ区切りリストです。クライアントがこのヘッダーを読み取ると、「このサイトは Viewport-Width
と Downlink
のクライアント ヒントを求めています。特定のヒントについて気にする必要はありません。これについては後ほど説明します。
これらのオプトイン ヘッダーは、任意のバックエンド言語で設定できます。たとえば、PHP の header
関数を使用できます。これらのオプトイン ヘッダーは、<meta>
タグの http-equiv
属性を使用して設定することもできます。
<meta http-equiv="Accept-CH" content="Viewport-Width, Downlink" />
ヒントをお伝えします。
Client Hints は、ユーザーが使用しているデバイスと、サイトへのアクセスに使用しているネットワークのいずれかを表します。使用可能なヒントをすべて簡単に説明します。
デバイスに関するヒント
一部のクライアント ヒントは、ユーザーのデバイスの特性(通常は画面の特性)を表します。これらの機能の一部は、特定のユーザーの画面に最適なメディア リソースを選択するのに役立ちますが、必ずしもメディア中心であるとは限りません。
このリストに入る前に、画面とメディアの解像度を説明するために使用される主な用語をいくつかご紹介します。
固有のサイズ: メディア リソースの実際のサイズ。たとえば、Photoshop で画像を開くと、画像サイズ ダイアログに表示される寸法は、その画像の固有のサイズを表します。
密度補正済みの固有サイズ: ピクセル密度が補正された後のメディア リソースのサイズ。画像の本質的なサイズをデバイスのピクセル比で割った値です。たとえば、次のマークアップについて考えてみましょう。
<img
src="whats-up-1x.png"
srcset="whats-up-2x.png 2x, whats-up-1x.png 1x"
alt="I'm that image you wanted."
/>
この場合、1x
画像の固有サイズが 320x240、2x
画像の固有サイズが 640x480 であるとします。このマークアップが、画面デバイスのピクセル比が 2 のデバイス(Retina 画面など)にインストールされたクライアントによって解析されると、2x
画像がリクエストされます。2x
画像の密度補正された固有サイズは 320x240 です。これは、640x480 を 2 で割ると 320x240 になるためです。
外部サイズ: CSS やその他のレイアウト要素(width
属性や height
属性など)が適用された後のメディア リソースのサイズ。密度補正された元のサイズが 320x240 の画像を読み込む <img>
要素があるとします。この要素には、256px
と 192px
の値がそれぞれ適用された CSS width
プロパティと height
プロパティも含まれています。この例では、その <img>
要素の外部サイズは 256x192 になります。
用語を理解したところで、デバイス固有のクライアント ヒントのリストを見てみましょう。
ビューポートの幅
Viewport-Width
は、ユーザーのビューポートの幅(CSS ピクセル単位)です。
Viewport-Width: 320
このヒントは、他の画面固有のヒントとともに使用して、特定の画面サイズ(アート ディレクション)に最適な画像のさまざまな処理(切り抜き)を提供したり、現在の画面幅に不要なリソースを省略したりできます。
DPR
DPR
(デバイス ピクセル比の略)は、ユーザーの画面の物理ピクセル数と CSS ピクセル数の比率を報告します。
DPR: 2
このヒントは、画面のピクセル密度に対応する画像ソースを選択する場合に役立ちます(srcset
属性の x
記述子など)。
幅
Width
ヒントは、sizes
属性を使用して <img>
タグまたは <source>
タグによって呼び出された画像リソースのリクエストに表示されます。sizes
はリソースの外部サイズをブラウザに伝えます。Width
はその外部サイズを使用して、現在のレイアウトに最適な固有のサイズの画像をリクエストします。
たとえば、ユーザーが 320 CSS ピクセルの幅の画面と DPR 2 のページをリクエストしたとします。デバイスは、sizes
属性値が 85vw
の <img>
要素を含むドキュメントを読み込みます(すべての画面サイズでビューポート幅の 85% です)。Width
ヒントがオプトインされている場合、クライアントは <img>
の src
のリクエストでこの Width
ヒントをサーバーに送信します。
Width: 544
この場合、クライアントは、リクエストされた画像の最適な固有の幅は、ビューポートの幅(272 ピクセル)の 85% に画面の DPR(2)を掛けた値(544 ピクセル)であることをサーバーにヒントしています。
このヒントは、画面の密度補正された幅を考慮するだけでなく、この重要な情報をレイアウト内の画像の外部サイズと調整するため、特に強力です。これにより、サーバーは画面とレイアウトの両方に最適な画像レスポンスをネゴシエートできます。
Content-DPR
画面にはデバイスのピクセル比があることはすでにご存じでしょうが、リソースにも独自のピクセル比があります。最も単純なリソース選択のユースケースでは、デバイスとリソースのピクセル比を同じにできます。でも!DPR
ヘッダーと Width
ヘッダーの両方が使用されている場合、リソースの外部サイズによって、2 つのサイズが異なるシナリオが発生する可能性があります。ここで役立つのが Content-DPR
ヒントです。
他のクライアント ヒントとは異なり、Content-DPR
はサーバーが使用するリクエスト ヘッダーではなく、DPR
ヒントと Width
ヒントを使用してリソースを選択するたびにサーバーが送信するレスポンス ヘッダーです。必須です。Content-DPR
の値は、次の式の結果にする必要があります。
Content-DPR
= [選択した画像リソースのサイズ] ÷([Width
] ÷ [DPR
])
Content-DPR
リクエスト ヘッダーが送信されると、ブラウザは画面のデバイスのピクセル比とレイアウトに合わせて指定された画像をスケーリングする方法を把握します。これを指定しないと、画像が正しくスケーリングされない可能性があります。
Device-Memory
厳密には、Device Memory API の一部である Device-Memory
は、現在のデバイスのおおよそのメモリ量を GiB 単位で表示します。
Device-Memory: 2
このヒントのユースケースとしては、メモリが限られているデバイスのブラウザに送信される JavaScript の量を減らすことが考えられます。JavaScript は、ブラウザが通常読み込むコンテンツ タイプの中で最もリソースを消費するタイプであるためです。あるいは、デコードでメモリの使用量が少ないため、DPR が低い画像を送信することもできます。
ネットワークのヒント
Network Information API は、ユーザーのネットワーク接続のパフォーマンスを説明するクライアント ヒントの別のカテゴリを提供します。これらは、最も有用なヒントセットだと思います。接続速度が遅い場合にクライアントにリソースを配信する方法を変更することで、ユーザーに合わせたエクスペリエンスを提供できます。
RTT
RTT
ヒントは、アプリケーション レイヤでのラウンドトリップ時間の概算(ミリ秒単位)を提供します。RTT
ヒントには、トランスポート レイヤの RTT とは異なり、サーバー処理時間が含まれます。
RTT: 125
読み込みパフォーマンスにおいてレイテンシが果たす役割があるため、このヒントが役立ちます。RTT
ヒントを使用すると、ネットワークの応答性に基づいて意思決定を行うことができます。これにより、(一部のリクエストを省略するなどして)エクスペリエンス全体の配信を高速化できます。
ダウンリンク
読み込みのパフォーマンスにおいてレイテンシは重要ですが、帯域幅も影響します。Downlink
ヒントは、メガビット / 秒(Mbps)で表され、ユーザーの接続の下り(ダウンロード)速度の概算を示します。
Downlink: 2.5
RTT
と組み合わせて使用すると、ネットワーク接続の品質に基づいてユーザーにコンテンツを配信する方法を変えるのに役立ちます。
エクアドル時間(ECT)
ECT
ヒントは有効な接続タイプ(Effective Connection Type)を表します。値は、接続タイプの列挙リストのいずれかです。各接続タイプは、RTT
値と Downlink
値の両方の指定された範囲内の接続を表します。
このヘッダーには、実際の接続タイプは表示されません。たとえば、ゲートウェイが携帯電話の基地局か Wi-Fi アクセス ポイントかについては表示されません。代わりに、現在の接続のレイテンシと帯域幅を分析し、最も似ているネットワーク プロファイルを特定します。たとえば、Wi-Fi 経由で低速ネットワークに接続すると、ECT
に 2g
の値が入力されることがあります。これは、有効な接続に最も近い近似値です。
ECT: 2g
ECT
の有効な値は、4g
、3g
、2g
、slow-2g
です。このヒントは、接続品質の評価の出発点として使用できます。その後、RTT
ヒントと Downlink
ヒントを使用して精度を高めることができます。
Save-Data
Save-Data
は、ネットワーク状態を説明するヒントではなく、ページから送信されるデータを減らすというユーザーの好みによるものです。
Save-Data
は、他のネットワーク ヒントに似ているため、ネットワーク ヒントとして分類することをおすすめします。また、レイテンシが高い / 帯域幅が低い環境で有効にする可能性もあります。このヒントは、存在する場合は常に次のようになります。
Save-Data: on
Google では、Save-Data
でできることについて説明してきました。パフォーマンスへの影響は多大なものになり得ます。これは、ユーザーが文字通り、送信するコンテンツを減らして欲しいと伝えているシグナルです。こうしたシグナルをリッスンして対応すれば、ユーザーは喜ぶでしょう。
すべてを組み合わせる
クライアント ヒントに対して行う処理は、ユーザーの判断に委ねられます。提供される情報量が多いため 選択肢も多くなりますアイデアを生み出すために、クライアントヒントが、アッパー ミッドウェストの田舎にある架空の木材会社 Sconnie Timber に対して何ができるかを見てみましょう。遠隔地ではよくあることですが、ネットワーク接続は不安定になることがあります。このような状況では、クライアント ヒントなどのテクノロジーがユーザーにとって大きな違いを生む可能性があります。
レスポンシブ画像
最もシンプルなレスポンシブ画像のユースケースを除き、複雑になる可能性があります。同じ画像に異なる画面サイズ、および異なる形式について、複数の処理とバリエーションがある場合はどうすればよいでしょうか。このマークアップは、非常に複雑になり、非常に迅速に複雑になります。間違えやすく、重要なコンセプト(sizes
など)を忘れたり誤解したりする可能性があります。
<picture>
と srcset
は間違いなく優れたツールですが、複雑なユースケースでは開発とメンテナンスに時間がかかります。マークアップの生成は自動化できますが、<picture>
と srcset
が提供する機能は複雑であるため、自動化は柔軟性を維持する方法で行う必要があります。
Client Hints はこれを簡素化できます。クライアント ヒントを使用して画像レスポンスをネゴシエートすると、次のようになります。
- ワークフローに応じて、
Viewport-Width
ヒントを確認して画像処理(アート画像など)を選択します。 Width
ヒントとDPR
ヒントをチェックし、画像のレイアウトサイズと画面密度に合ったソースを選択して、画像の解像度を選択します(srcset
でx
記述子とw
記述子が機能する仕組みに似ています)。- ブラウザがサポートするファイル形式のうち、最も最適なものを選択します(
Accept
は、ほとんどのブラウザでこの作業を支援します)。
架空の木材会社クライアントの場合、クライアント ヒントを使用した PHP で単純なレスポンシブ画像選択ルーティンを開発しました。つまり、このマークアップをすべてのユーザーに送信する代わりに、次のようにします。
<picture>
<source
srcset="
company-photo-256w.webp 256w,
company-photo-512w.webp 512w,
company-photo-768w.webp 768w,
company-photo-1024w.webp 1024w,
company-photo-1280w.webp 1280w
"
type="image/webp"
/>
<img
srcset="
company-photo-256w.jpg 256w,
company-photo-512w.jpg 512w,
company-photo-768w.jpg 768w,
company-photo-1024w.jpg 1024w,
company-photo-1280w.jpg 1280w
"
src="company-photo-256w.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="The Sconnie Timber Staff!"
/>
</picture>
個々のブラウザのサポートに基づいて、以下に絞り込むことができました。
<img
src="/image/sizes:true/company-photo.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="SAY CHEESY PICKLES."
/>
この例では、/image
URL は PHP スクリプトであり、その後に mod_rewrite によって書き換えられたパラメータが続きます。画像ファイル名と追加のパラメータを受け取り、バックエンド スクリプトが特定の条件で最適な画像を選択できるようにします。
最初の質問は「これはバックエンドで <picture>
と srcset
を再実装しているだけではないですか?」ということでしょう。
ある意味では、そうです。ただし、重要な違いがあります。アプリケーションがクライアント ヒントを使用してメディア レスポンスを作成する場合、ほとんど(すべてではない)の作業を自動化できます。この作業には、代わりにこの作業を行うサービス(CDN など)を含めることができます。一方、HTML ソリューションでは、すべてのユースケースに対応するために新しいマークアップを記述する必要があります。はい、マークアップの生成は自動化できます。ただし、設計や要件が変わった場合は、将来、自動化戦略を見直す必要が生じる可能性が高くなります。
クライアント ヒントを使用すると、ロスレスの高解像度画像から始め、画面とレイアウトの任意の組み合わせに最適なように動的にサイズを変更できます。ブラウザが選択できる画像候補の固定リストを列挙する必要がある srcset
とは異なり、このアプローチはより柔軟に使用できます。srcset
では、256w
、512w
、768w
、1024w
など、粗いバリエーション セットをブラウザに提供する必要がありますが、クライアント ヒントを使用したソリューションでは、大量のマークアップを使用せずにすべての幅を提供できます。
もちろん、画像選択ロジックを自分で記述する必要はありません。Cloudinary は、w_auto
パラメータの使用時にクライアント ヒントを使用して画像レスポンスを作成します。クライアント ヒントをサポートするブラウザを使用した場合、ユーザーの中央値でダウンロード バイト数が 42% 削減されました。
でも気をつけて!デスクトップ版 Chrome 67 の変更により、クロスオリジン クライアント ヒントのサポートが削除されました。幸い、これらの制限はモバイル版 Chrome には影響しません。また、機能ポリシーの作業が完了すると、すべてのプラットフォームで完全に解除されます。
ネットワーク速度が遅いユーザーをサポートする
適応型パフォーマンスとは、クライアントのヒントによって提供される情報、具体的にはユーザーのネットワーク接続の現在の状態に関する情報に基づいて、リソースの配信方法を調整できるという考え方です。
Sconnie Timber のサイトでは、ネットワークが遅いときに負荷を軽減するために、バックエンド コードで Save-Data
、ECT
、RTT
、Downlink
ヘッダーが検査されます。処理が完了すると、ネットワーク品質スコアが生成されます。このスコアは、ユーザー エクスペリエンスを改善するために介入する必要があるかどうかを判断するために使用できます。このネットワーク スコアは 0
~1
の範囲で、0
はネットワークの品質が最悪で、1
は最良です。
最初に、Save-Data
が存在するかどうかを確認します。有効な場合、スコアは 0
に設定されます。これは、ユーザーがエクスペリエンスを軽量化、高速化するために必要なことは何でも行いたいと想定しているためです。
Save-Data
がない場合、ECT
、RTT
、Downlink
のヒントの値を重み付けして、ネットワーク接続品質を示すスコアを計算します。ネットワーク スコア生成ソースコードは GitHub で入手できます。重要なポイントは、ネットワーク関連のヒントをなんらかの方法で使用すると、低速のネットワークを利用するユーザーのためにエクスペリエンスを改善できるということです。
サイトがクライアント ヒントから提供される情報に適応する場合は、「すべてまたはなし」のアプローチを採用する必要はありません。送信するリソースをインテリジェントに決定できます。レスポンシブ画像選択ロジックを変更して、特定のディスプレイに低品質の画像を送信し、ネットワーク品質が低い場合に読み込みパフォーマンスを高速化できます。
この例では、低速ネットワーク上のサイトのパフォーマンスを改善する際のクライアント ヒントの影響を把握できます。以下は、クライアントのヒントに適応しない低速ネットワーク上のサイトの WebPagetest ウォーターフォールです。
次に、同じ低速接続で同じサイトのウォーターフォールを表示しますが、今回はクライアントのヒントを使用して重要でないページリソースを除外します。
クライアント ヒントにより、ページの読み込み時間が 45 秒以上からその 10 分の 1 未満に短縮されました。このシナリオでのクライアント ヒントのメリットは非常に大きく、遅いネットワークで重要な情報を探しているユーザーにとって大きなメリットとなります。
また、クライアント ヒントをサポートしていないブラウザでのエクスペリエンスを損なうことなく、クライアントのヒントを使用できます。たとえば、サポートされていないブラウザでも完全なエクスペリエンスを提供しながら、ECT
ヒントの値を使用してリソース配信を調整する場合は、次のようにデフォルト値にフォールバックするように設定できます。
// Set the ECT value to "4g" by default.
$ect = isset($_SERVER["HTTP_ECT"]) ? $_SERVER["HTTP_ECT"] : "4g";
ここで、"4g"
は ECT
ヘッダーで説明されている最高品質のネットワーク接続を表します。$ect
を "4g"
に初期化すると、クライアント ヒントをサポートしていないブラウザは影響を受けません。オプトインしましょう!
キャッシュに注意してください。
HTTP ヘッダーに基づいてレスポンスを変更するときは、そのリソースの今後の取得がキャッシュでどのように処理されるかに注意する必要があります。ここでは、キャッシュ エントリを指定されたリクエスト ヘッダーの値にキー付けするため、Vary
ヘッダーが不可欠です。簡単に言うと、特定の HTTP リクエスト ヘッダーに基づいてレスポンスを変更する場合、ほとんどの場合、そのヘッダーのリクエストは次のように Vary
に含める必要があります。
Vary: DPR, Width
ただし、これには大きな注意点があります。頻繁に変更されるヘッダー(Cookie
など)でキャッシュに保存可能なレスポンスを Vary
することはしないでください。これらのリソースが実質的にキャッシュに保存できなくなるためです。RTT
や Downlink
などのクライアント ヒント ヘッダーで Vary
を使用しないようにすることをおすすめします。これらのヘッダーは、接続要因であり、頻繁に変更される可能性があるためです。これらのヘッダーのレスポンスを変更する場合は、ECT
ヘッダーのみをキーにすることを検討してください。これにより、キャッシュミスが最小限に抑えられます。
もちろん、これはレスポンスをキャッシュに保存する場合にのみ適用されます。たとえば、HTML アセットが動的な場合は、HTML アセットをキャッシュに保存しません。再訪問時にユーザー エクスペリエンスが損なわれる可能性があるためです。このような場合は、必要に応じて自由に回答を変更し、Vary
を気にする必要はありません。
サービス ワーカーのクライアント ヒント
コンテンツのネゴシエーションは、もはやサーバーの専用ではありません。Service Worker はクライアントとサーバー間のプロキシとして機能するため、JavaScript を介してリソースを配信する方法を制御できます。これにはクライアント ヒントも含まれます。サービス ワーカーの fetch
イベントでは、event
オブジェクトの request.headers.get
メソッドを使用して、リソースのリクエスト ヘッダーを次のように読み取ることができます。
self.addEventListener('fetch', (event) => {
let dpr = event.request.headers.get('DPR');
let viewportWidth = event.request.headers.get('Viewport-Width');
let width = event.request.headers.get('Width');
event.respondWith(
(async function () {
// Do what you will with these hints!
})(),
);
});
オプトインした Client Hints ヘッダーは、この方法で読み取ることができます。ただし、この方法で得られる情報は一部に限られます。ネットワーク固有のヒントは、navigator
オブジェクトの同等の JavaScript プロパティで読み取ることができます。
クライアント ヒント | JS の同等関数 |
---|---|
「ECT」 | 「navigator.connection.effectiveType」 |
RTT | navigator.connection.rtt |
「Save-Data」 | `navigator.connection.saveData` |
`Downlink` | navigator.connection.downlink |
`Device-Memory` | `navigator.deviceMemory` |
これらの API はすべての地域で利用できないため、in
演算子を使用して機能チェックする必要があります。
if ('connection' in navigator) {
// Work with netinfo API properties in JavaScript!
}
サーバー側で使用するロジックと同様のものを使用できます。ただし、クライアント ヒントを使用してコンテンツをネゴシエートするサーバーが必要ないことが異なります。Service Worker は、ユーザーがオフラインの場合でもコンテンツを提供できる追加機能があるため、エクスペリエンスを高速化および強化できます。
まとめ
Client Hints を使用すると、ユーザー エクスペリエンス全体を段階的に高速化できます。特に複雑なユースケースでは、<picture>
と srcset
に依存するよりもレスポンシブ画像の配信が容易になるように、ユーザーのデバイスの機能に基づいてメディアを配信できます。これにより、開発側の負担と時間を軽減できるだけでなく、
さらに重要なのは、送信内容と送信方法を変更することで、ネットワーク接続の不具合を検出し、ユーザーの不満を解消できることです。これにより、不安定なネットワーク上のユーザーがサイトに簡単にアクセスできるようになります。Service Worker と組み合わせることで、オフラインで利用できる非常に高速なサイトを作成できます。
クライアント ヒントは Chrome(および Chromium ベースのブラウザ)でのみ使用できますが、サポートしていないブラウザに負担がかからない方法で使用することが可能です。クライアント ヒントを使用して、すべてのユーザーのデバイス機能と接続先のネットワークを考慮した、真にインクルーシブで適応性に優れたエクスペリエンスを作成することを検討してください。他のブラウザ ベンダーもその価値を認識し、実装の意向を示してくれることを願っています。
リソース
- クライアント ヒントによる自動レスポンシブ画像
- クライアント ヒントとレスポンシブ画像 - Chrome 67 での変更点
- (クライアントの)ヒントを活用しましょう。(スライド)
Save-Data
を使用した高速で軽量なアプリケーションの提供
この記事に関する貴重なフィードバックと編集をしてくださった Ilya Grigorik 氏、Eric Portis 氏、Jeff Posnick 氏、Yoav Weiss 氏、Estelle Weyl 氏に感謝いたします。