どこでも高速に動作するサイトを開発することは、難しい課題となる可能性があります。デバイスの機能や接続先のネットワークの品質は多種多様であるため、これは克服できない課題のように思えるかもしれません。ブラウザの機能を利用して読み込みパフォーマンスを向上させることはできますが、ユーザーのデバイスの機能やネットワーク接続の品質を把握するにはどうすればよいでしょうか。この問題を解決するのが、Client Hints です。
Client Hints は、オプトイン HTTP リクエスト ヘッダーのセットであり、ユーザーのデバイスと接続先のネットワークのこれらの側面に関する分析情報を提供します。サーバーサイドでこの情報を利用することで、デバイスやネットワークの状況に応じてコンテンツの配信方法を変更できます。これにより、よりインクルーシブなユーザー エクスペリエンスを実現できます。
すべてはコンテンツ ネゴシエーション
Client Hints は、コンテンツ ネゴシエーションのもう 1 つの方法です。つまり、ブラウザのリクエスト ヘッダーに基づいてコンテンツ レスポンスを変更します。
コンテンツ ネゴシエーションの一例として、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 と同様に、Client Hints はコンテンツをネゴシエーションするためのもう 1 つの方法ですが、デバイスの機能とネットワークの状態のコンテキストで使用されます。Client Hints を使用すると、ユーザーの個々のエクスペリエンスに基づいて、サーバーサイドのパフォーマンスに関する決定を行うことができます。たとえば、ネットワークの状態が悪いユーザーに重要でないリソースを提供するかどうかを決定できます。このガイドでは、利用可能なすべてのヒントと、それらを使用してコンテンツ配信をユーザーにとってより使いやすいものにする方法について説明します。
オプトイン
Accept ヘッダーとは異なり、Client Hints は魔法のように現れるわけではありません(後述の Save-Data を除く)。リクエスト ヘッダーを最小限に抑えるため、ユーザーがリソースをリクエストしたときに Accept-CH ヘッダーを送信して、受信する Client Hints をオプトインする必要があります。
Accept-CH: Viewport-Width, Downlink
Accept-CH の値は、後続のリソース リクエストの結果を決定するためにサイトが使用する、リクエストされたヒントのカンマ区切りのリストです。クライアントがこのヘッダーを読み取ると、「このサイトは Viewport-Width と Downlink の Client Hints を必要としている」と認識します。具体的なヒント自体については心配する必要はありません。これらについては後ほど説明します。
これらのオプトイン ヘッダーは、任意のバックエンド言語で設定できます。たとえば、PHP の header 関数を使用できます。<meta> タグの http-equiv 属性を使用して、これらのオプトイン ヘッダーを設定することもできます。
<meta http-equiv="Accept-CH" content="Viewport-Width, Downlink" />
すべての Client Hints
Client Hints は、ユーザーが使用しているデバイスと、サイトへのアクセスに使用しているネットワークのいずれかを示します。利用可能なヒントをすべて簡単に説明します。
デバイスに関するヒント
一部の 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> 要素があり、CSS の width プロパティと height プロパティの値がそれぞれ 256px と 192px であるとします。この例では、<img> 要素の外部サイズは 256x192 になります。
width: 256px; と height: 192px; の CSS ルールを適用すると、320x240 の固有サイズの画像が 256x192 の外部サイズに変換されます。用語を理解したところで、利用可能なデバイス固有の Client Hints のリストを見ていきましょう。
Viewport-Width
Viewport-Width は、ユーザーのビューポートの幅(CSS ピクセル単位)です。
Viewport-Width: 320
このヒントを他の画面固有のヒントと組み合わせて使用すると、特定の画面サイズに最適な画像のさまざまな処理(切り抜きなど)を提供したり(アート ディレクション)、現在の画面幅に不要なリソースを省略したりできます。
DPR
DPR(デバイス ピクセル比の略)は、ユーザーの画面の物理ピクセルと CSS ピクセルの比率をレポートします。
DPR: 2
このヒントは、画面のピクセル密度に対応する画像ソースを選択する場合に役立ちます(srcset 属性の x 記述子など)。
幅
Width ヒントは、sizes 属性を使用して <img> タグまたは <source> タグによって呼び出された画像リソースのリクエストに表示されます。sizes は、リソースの外部サイズをブラウザに伝えます。Width は、その外部サイズを使用して、現在のレイアウトに最適な固有サイズを持つ画像をリクエストします。
たとえば、ユーザーが DPR が 2 の 320 CSS ピクセル幅の画面でページをリクエストしたとします。デバイスは、85vw の sizes 属性値を含む <img> 要素を含むドキュメントを読み込みます(すべての画面サイズでビューポート幅の 85%)。Width ヒントがオプトインされている場合、クライアントは <img> の src のリクエストとともに、この Width ヒントをサーバーに送信します。
Width: 544
この場合、クライアントはサーバーに対して、リクエストされた画像の最適な固有幅は、ビューポート幅(272 ピクセル)に画面の DPR(2)を掛けた 544 ピクセルであることを示唆しています。
このヒントは、画面の密度補正された幅を考慮するだけでなく、この重要な情報をレイアウト内の画像の外部サイズと調整するため、特に強力です。これにより、サーバーは画面とレイアウトの両方に最適な画像レスポンスをネゴシエートできます。
Content-DPR
画面にデバイスのピクセル比があることはすでにご存じかと思いますが、リソースにも独自のピクセル比があります。最も単純なリソース選択のユースケースでは、デバイスとリソースのピクセル比が同じになることがあります。でもね!DPR ヘッダーと Width ヘッダーの両方が使用されている場合、リソースの外部サイズによって、2 つが異なるシナリオが発生する可能性があります。ここで Content-DPR ヒントが役立ちます。
他の Client Hints とは異なり、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 は、ユーザーのネットワーク接続のパフォーマンスを記述する別のカテゴリの Client Hints を提供します。これらは、最も有用なヒントのセットだと思います。これらを使用すると、低速接続のクライアントにリソースを配信する方法を変更することで、ユーザーに合わせてエクスペリエンスを調整できます。
RTT
RTT ヒントは、アプリケーション レイヤでのラウンドトリップ時間の概算値(ミリ秒単位)を提供します。RTT ヒントには、トランスポート層の RTT とは異なり、サーバーの処理時間が含まれます。
RTT: 125
このヒントは、読み込みパフォーマンスにおけるレイテンシの役割を考慮すると有用です。RTT ヒントを使用すると、ネットワークの応答性に基づいて判断を下すことができます。これにより、エクスペリエンス全体の配信を高速化できます(一部のリクエストを省略するなど)。
ダウンリンク
読み込みパフォーマンスではレイテンシが重要ですが、帯域幅も影響します。Downlink ヒントは、メガビット/秒(Mbps)で表され、ユーザーの接続のおおよそのダウンストリーム速度を示します。
Downlink: 2.5
RTT と組み合わせて Downlink を使用すると、ネットワーク接続の品質に基づいて、ユーザーへのコンテンツの配信方法を変更できます。
エクアドル時間(ECT)
ECT ヒントは、Effective Connection Type(有効な接続タイプ)を表します。値は、接続タイプの列挙リストの 1 つです。各接続タイプは、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 でできることについて説明してきました。パフォーマンスに大きな影響を与える可能性があります。これは、ユーザーが文字どおり、送信するメールを減らすよう求めているシグナルです。そのシグナルに耳を傾けて対応すれば
ユーザーに喜ばれます
まとめ
Client Hints をどのように使用するかは、お客様次第です。提供される情報が多いため、多くのオプションがあります。アイデアを膨らませるために、米国中西部北部の農村地域にある架空の木材会社 Sconnie Timber で Client Hints がどのように役立つかを見てみましょう。遠隔地ではよくあることですが、ネットワーク接続が不安定になることがあります。ここで、Client Hints のようなテクノロジーがユーザーに大きな違いをもたらします。
レスポンシブ画像
最もシンプルなレスポンシブ画像のユースケース以外は、複雑になる可能性があります。複数のトリートメントと、画面サイズごとに異なる同じ画像のバリエーションと、異なるフォーマットがある場合はどうすればよいでしょうか。このマークアップは、非常に複雑で、非常に迅速です。間違えやすく、重要なコンセプト(sizes など)を忘れたり、誤解したりしやすい。
<picture> と srcset は間違いなく優れたツールですが、複雑なユースケースの開発とメンテナンスには時間がかかることがあります。マークアップの生成を自動化することもできますが、<picture> と srcset が提供する機能は複雑であるため、自動化は困難です。自動化は、提供される柔軟性を維持する方法で行う必要があります。
Client Hints を使用すると、この処理を簡素化できます。Client Hints を使用した画像レスポンスのネゴシエーションは、次のようになります。
- ワークフローに該当する場合は、まず
Viewport-Widthヒントをチェックして、画像処理(アート ディレクションされた画像など)を選択します。 WidthヒントとDPRヒントを確認し、画像のレイアウト サイズと画面密度に適合するソースを選択して、画像の解像度を選択します(srcsetでのx記述子とw記述子の仕組みと同様です)。- ブラウザがサポートする最適なファイル形式を選択します(
Acceptを使用すると、ほとんどのブラウザでこの処理を行うことができます)。
架空の木材会社のクライアントが懸念していた点について、私は Client Hints を使用する 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 ソリューションでは、すべてのユースケースに対応するために新しいマークアップを作成する必要があります。もちろん、マークアップの生成を自動化することは可能です。ただし、設計や要件が変更された場合は、将来的に自動化戦略を見直す必要が生じる可能性があります。
Client Hints を使用すると、可逆性の高解像度画像から始めて、画面とレイアウトの任意の組み合わせに最適になるように動的にサイズを変更できます。このアプローチは、ブラウザが選択できる画像候補の固定リストを列挙する必要がある srcset とは異なり、より柔軟です。srcset では、ブラウザにバリエーションの粗いセット(256w、512w、768w、1024w など)を提供する必要がありますが、クライアント ヒントを活用したソリューションでは、大量のマークアップを使用せずに、すべての幅に対応できます。
もちろん、画像選択ロジックを自分で作成する必要はありません。Cloudinary は、w_auto パラメータを使用する際に Client Hints を使用して画像レスポンスを作成します。Client Hints をサポートするブラウザを使用した場合、中央値のユーザーがダウンロードしたバイト数が 42% 減少したことが確認されています。
ただし、注意が必要です。パソコン版 Chrome 67 の変更により、クロスオリジンのクライアント ヒントのサポートが削除されました。幸いなことに、これらの制限はモバイル版の Chrome には影響しません。また、Feature Policy の作業が完了すると、すべてのプラットフォームで制限が解除されます。
低速ネットワークのユーザーをサポートする
アダプティブ パフォーマンスとは、Client Hints が提供する情報(具体的には、ユーザーのネットワーク接続の現在の状態に関する情報)に基づいて、リソースの配信方法を調整できるという考え方です。
Sconnie Timber のサイトでは、ネットワークが遅い場合に負荷を軽減する手順を講じており、Save-Data、ECT、RTT、Downlink ヘッダーがバックエンド コードで調べられます。この処理が完了すると、ネットワーク品質スコアが生成されます。このスコアを使用して、ユーザー エクスペリエンスを向上させるために介入すべきかどうかを判断できます。このネットワーク スコアは 0~1 の範囲で、0 はネットワーク品質が最悪、1 は最良であることを示します。
まず、Save-Data が存在するかどうかを確認します。有効になっている場合、スコアは 0 に設定されます。これは、ユーザーがエクスペリエンスをより軽量かつ高速にするために必要なことを行うことを望んでいると想定しているためです。
ただし、Save-Data がない場合は、ECT、RTT、Downlink のヒントの値を比較検討して、ネットワーク接続の品質を表すスコアを計算します。ネットワーク スコア生成のソースコードは GitHub で入手できます。重要なのは、ネットワーク関連のヒントを何らかの形で使用すれば、低速なネットワークを使用しているユーザーのエクスペリエンスを改善できるということです。
サイトが Client Hints から提供される情報に適応する場合、「すべてか無か」のアプローチを採用する必要はありません。送信するリソースをインテリジェントに決定できます。レスポンシブ画像の選択ロジックを変更して、ネットワーク品質が低い場合に、特定のディスプレイに低品質の画像を送信して読み込みパフォーマンスを高速化できます。
この例では、低速なネットワークでのサイトのパフォーマンスを改善するうえで、Client Hints がどのような効果をもたらすかを確認できます。以下は、Client Hints に対応していない低速ネットワーク上のサイトの WebPagetest ウォーターフォールです。
次に、同じ低速接続で同じサイトのウォーターフォールを示します。ただし、今回は、サイトが Client Hints を使用して、重要でないページリソースを排除しています。
Client Hints により、ページの読み込み時間が 45 秒以上から 10 分の 1 未満に短縮されました。このシナリオにおける Client Hints のメリットは、いくら強調しても足りないほどです。低速なネットワークで重要な情報を探しているユーザーにとって、大きなメリットとなる可能性があります。
さらに、Client Hints をサポートしていないブラウザのエクスペリエンスを損なうことなく、Client Hints を使用できます。たとえば、ECT ヒントの値を使用してリソース配信を調整し、サポートされていないブラウザでも完全なエクスペリエンスを提供したい場合は、次のようにデフォルト値にフォールバックするように設定できます。
// Set the ECT value to "4g" by default.
$ect = isset($_SERVER["HTTP_ECT"]) ? $_SERVER["HTTP_ECT"] : "4g";
ここで、"4g" は、ECT ヘッダーが記述する最高品質のネットワーク接続を表します。$ect を "4g" に初期化すると、クライアント ヒントをサポートしていないブラウザは影響を受けません。オプトイン FTW!
キャッシュに注意
HTTP ヘッダーに基づいてレスポンスを変更する場合は、キャッシュがそのリソースの今後のフェッチをどのように処理するかを認識する必要があります。Vary ヘッダーは、キャッシュ エントリをリクエスト ヘッダーの値にキー設定するため、ここでは不可欠です。簡単に言うと、特定の HTTP リクエスト ヘッダーに基づいてレスポンスを変更する場合は、次のように、そのヘッダーを Vary に含める必要があります。
Vary: DPR, Width
ただし、これには大きな注意点があります。頻繁に変更されるヘッダー(Cookie など)でキャッシュ可能なレスポンスを Vary することは避けてください。これらのリソースは実質的にキャッシュ不可になるためです。このため、RTT や Downlink などの Client Hints ヘッダーで Vary を行うことは避けることをおすすめします。これらは、頻繁に変更される可能性がある接続要素であるためです。これらのヘッダーのレスポンスを変更する場合は、ECT ヘッダーのみをキーにすることを検討してください。これにより、キャッシュミスを最小限に抑えることができます。
もちろん、これはそもそもレスポンスをキャッシュしている場合にのみ適用されます。たとえば、HTML アセットのコンテンツが動的な場合、そのアセットはキャッシュに保存されません。これは、ユーザーが繰り返しアクセスしたときにユーザー エクスペリエンスが損なわれる可能性があるためです。このような場合は、必要に応じて回答を自由に変更してください。Vary については気にする必要はありません。
Service Worker の Client Hints
コンテンツ ネゴシエーションは、もはやサーバーだけのものではありません。Service Worker はクライアントとサーバー間のプロキシとして機能するため、JavaScript を介したリソースの配信方法を制御できます。これには Client Hints が含まれます。サービス ワーカーの 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 プロパティで読み取ることができます。
| Client Hint | 同等の 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!
}
ここからは、サーバーで使用するロジックと同様のロジックを使用できます。ただし、Client Hints でコンテンツをネゴシエートするためにサーバーは必要ありません。サービス ワーカーは、ユーザーがオフラインのときにコンテンツを提供する機能が追加されているため、エクスペリエンスを高速化し、復元力を高めることができます。
まとめ
Client Hints を使用すると、完全にプログレッシブな方法でユーザー エクスペリエンスを高速化できます。ユーザーのデバイスの機能に基づいてメディアを提供できるため、特に複雑なユースケースでは、<picture> や srcset に依存するよりもレスポンシブな画像の提供が容易になります。これにより、開発側の時間と労力を削減できるだけでなく、
さらに重要なのは、ネットワーク接続が不安定なことを検知し、送信する内容と送信方法を変更することで、ユーザーの不便を解消できることです。これは、不安定なネットワーク上のユーザーがサイトにアクセスしやすくするうえで、非常に役立ちます。Service Worker と組み合わせることで、オフラインでも利用できる非常に高速なサイトを作成できます。
クライアント ヒントは Chrome と Chromium ベースのブラウザでのみ利用できますが、クライアント ヒントをサポートしていないブラウザに影響を与えない方法で使用できます。Client Hints を使用して、すべてのユーザーのデバイス機能と接続先のネットワークを認識する、真にインクルーシブで適応性の高いエクスペリエンスを作成することを検討してください。他のブラウザ ベンダーがそれらの価値を認識し、実装する意向を示すことを期待しています。
リソース
- Client Hints を使用した自動レスポンシブ画像
- Client Hints とレスポンシブ画像 - Chrome 67 での変更点
- Client Hints を活用する (スライド)
Save-Dataを使用して高速で軽量なアプリケーションを配信する
この記事に貴重なフィードバックと編集を提供してくれた Ilya Grigorik、Eric Portis、Jeff Posnick、Yoav Weiss、Estelle Weyl に感謝します。