2022 年 5 月、Aurora と Angular のチームは、Angular の画像ディレクティブの共同作成を発表しました。このディレクティブは、Angular v14.2 の一環としてデベロッパー プレビューとして最近リリースされました。この投稿では、新しい画像ディレクティブ NgOptimizedImage
が Angular での画像最適化をサポートする方法について説明します。
背景
画像はウェブのユーザー エクスペリエンスに欠かせない重要な要素であり、ウェブページの 99.9% で 1 つ以上の画像に対するリクエストが生成されています。また、画像はページ容量の最大の原因であり、1 ページあたり 982 KB の中央値を占めています。
画像の数とサイズが増大しているため、画像はウェブページのパフォーマンスを妨げ、ウェブに関する主な指標の指標に影響する可能性があります。パソコン用ページの 79.4% で、2021 年に画像は Largest Contentful Paint(LCP)要素でした。このように、最適化された画像を求めることは、多くの人にとって絶え間ない努力となっています。
Aurora のチームは、フレームワークの力を活用して、開発者の一般的な課題を解決する組み込みソリューションを提供できると考えています。画像最適化の分野に初めて進出したのは、Next.js 画像コンポーネントでした。同社は、このコンポーネントが、画像最適化のデベロッパー エクスペリエンス(DX)を改善することで、フレームワークを使用するより多くのアプリでパフォーマンス向上につながるかどうかをテストする場であると考えました。
Next.js ユーザーである Leboncoin が最初に投稿した結果は、心強いものでした。Leboncoin では、next/image
の使用を開始してから LCP が大幅に改善されました(2.4 秒から 1.7 秒に)。その後のコミュニティでの next/image
の採用が、LCP のしきい値を満たす Next.js オリジンの増加の一端を担いました。まもなく、他のフレームワークでも同様の機能に対するリクエストが寄せられ、そのうちの 1 つが Angular でした。
その結果、Aurora は Angular と Nuxt に相談し、これらのフレームワーク用にイメージ コンポーネントのプロトタイプを作成することにしました。Nuxt イメージ コンポーネントは昨年リリースされました。このたび、画像最適化のデフォルトを Angular に導入する、Angular 画像ディレクティブ(NgOptimizedImage
)がリリースされました。
機会
Angular は、現在デベロッパーが使用している主要な JavaScript フレームワークの一つです。HTTPArchive モバイルでクロールされた 5 万以上のオリジンに使用されており、NPM では週あたり 300 万回近くダウンロードされています。
Core Web Vitals のスコアを見ると、LCP の「良好」なしきい値を満たす Angular オリジンの割合はまだ改善が必要です。Angular サイトのうち、2022 年 6 月のモバイル LCP が良好だったのはわずか 18.74%モバイルとパソコンのウェブページの 70% 以上で画像は LCP 要素であるため、LCP 画像が最適化されていないことが Angular ウェブサイトの LCP が低下する主な原因の 1 つである可能性があります。
Angular 画像に関するディレクティブは、これらの数値を改善できるように設計されています。
NgOptimizedImage ディレクティブの MVP
Angular 画像ディレクティブの MVP は、Aurora がこれまでに構築した画像コンポーネントから得た教訓に基づいており、デザインを Angular のクライアントサイド レンダリング エクスペリエンスに適応させています。標準的な画像最適化の問題の多くは、次のいずれかによって対処されています。
- 強力なデフォルトを指定する。
- ベスト プラクティスへの準拠を確認するためのエラーまたは警告をスローします。
この設計の要点は次のとおりです。
インテリジェントな遅延読み込み
ページの読み込み時にユーザーには見えない画像(スクロールしなければ見えない範囲の画像、非表示のカルーセル画像など)は、遅延読み込みにするのが理想的です。遅延読み込みを使用すると、ブラウザのリソースを解放し、他の重要なテキスト、メディア、スクリプトを読み込むことができます。ほとんどの画像は重要性が低く、遅延読み込みを行うべきですが、2021 年にはネイティブ遅延読み込みを使用したのはページの 7.8% のみでした。
Angular の画像ディレクティブは、デフォルトでは重要性の低い画像を遅延読み込みし、
priority
とマークされた画像のみを積極的に読み込みます。これにより、ほとんどの画像で最適な読み込み動作が実現します。重要なイメージの優先順位付け
リソースヒントの追加(
preload
やpreconnect
など)を使用して重要な画像の読み込みを優先することが、推奨されるベスト プラクティスです。しかし、ほとんどのアプリは使用していません。2021 年のウェブ アルマナックによると、事前接続ヒントを使用しているのはわずか 12.7% で、プリロード ヒントを使用しているのはわずか 22.1% です。画像ディレクティブは、画像が優先度としてマークされている場合に、2 つの面で機能します。
- 画像の fetchpriority を
"high"
に設定することで、ブラウザは高い優先度で画像をダウンロードする必要があることを認識できます。 - Development Mode では、イメージのオリジンに対応する
preconnect
リソースヒントが含まれていることをランタイム チェックが確認します。
開発モードでは、PerformanceObserver API も使用して、LCP 画像が
priority
とマークされていることを確認します。priority
とマークされていない場合はエラーがスローされ、LCP イメージにpriority
属性を追加するようデベロッパーに指示されます。最終的には、この自動化と適合性の組み合わせにより、LCP イメージに
preconnect
ヒントとfetchpriority
属性値high
が設定され、遅延読み込みが行われないことが保証されます。- 画像の fetchpriority を
一般的なイメージツール用に最適化された構成
Angular アプリケーションでは、デフォルトで最適化サービスを提供する画像 CDN を使用することをおすすめします。
このディレクティブでは、アプリ内で画像 CDN を設定できるデベロッパー エクスペリエンス(DX)が特に優れているため、画像 CDN の使用が推奨されます。設定で CDN プロバイダとベース URL を定義できるローダ API をサポートしています。設定が完了したら、あとはマークアップでアセット名を定義するだけです。たとえば
// in module providers: provideImgixLoader('https://mysite.net/assets/') // in markup <img ngSrc="image.png" > <img ngSrc="image2.png" >
これは、次の画像タグを含めるのと同じことです。これにより、デベロッパーがすべての画像に含める必要があるマークアップが少なくなります。
<img src="https://mysite.net/assets/image.png"> <img src="https://mysite.net/assets/image2.png">
image ディレクティブは、よく使用される画像 CDN に最適な設定を組み込みローダーに提供します。これらのローダーは、各 CDN で推奨される画像形式と圧縮設定が使用されるように、画像 URL を自動的にフォーマットします。
組み込みエラーと警告
上記の組み込みの最適化に加えて、このディレクティブには、デベロッパーが画像マークアップの推奨ベスト プラクティスに従っていることを確認するチェックも組み込まれています。イメージ ディレクティブは、以下のチェックを実行します。
サイズ設定されていない画像: 画像のマークアップで幅と高さが明示的に定義されていない場合、画像ディレクティブはエラーをスローします。サイズ調整されていない画像によりレイアウト シフトが発生し、ページの Cumulative Layout Shift(CLS)指標が影響を受けます。これを防ぐためのおすすめの方法は、画像に
width
属性とheight
属性を指定することです。アスペクト比: HTML で定義された
width
:height
のアスペクト比がレンダリングされた画像の実際のアスペクト比と同等でない場合、image ディレクティブでエラーがスローされます。このため、画面上で画像がゆがんで見えることがあります。このような状況は、- 誤って間違ったサイズ(幅または高さ)を定義した
- CSS で割合によって一方の寸法を定義していて、他方の寸法は定義していない場合(たとえば、
width: 100%
で画像を拡大縮小するにはheight: auto
を指定する必要があります)。
サイズ超過の画像: 画像で
srcset
が定義されておらず、固有の画像がレンダリングされた画像よりも大幅に大きい場合、ディレクティブはsrcset
属性とsizes
属性の使用を提案する警告を表示します。画像密度: ピクセル密度が
3x
を超える画像をsrcset
に含めようとすると、ディレクティブでエラーがスローされます。2x
よりも大きな記述子は、通常おすすめしません。高解像度のモバイル デバイスで大きな画像をダウンロードせざるを得なくなるという意図しない結果が生じるためです。さらに、人間の目では、2 倍以上の差を見分けることができません。
チャレンジ
NgOptimizedImage
を設計する際の主な課題は、画像最適化戦略をクライアントサイドのフレームワーク内で機能させることでした。Next.js のデフォルトのレンダリング エクスペリエンスは、サーバーサイド レンダリング(SSR)または静的サイト生成(SSG)ですが、Angular ではクライアント サイド レンダリング(CSR)です。Angular は SSR ライブラリ(Angular/Universal)をサポートしていますが、ほとんどの Angular アプリ(60% 程度)は CSR を使用しています。
image ディレクティブは、Angular アプリの一般的なユースケースに合わせて CSR 用に作られています。これにより新たな制約が生じ、チームは CSR アプリに特化した最適化の構築方法を再考する必要がありました。
直面した課題には、次のようなものがあります。
サポートするリソースヒント
重要なアセットをプリロードすることで、ブラウザがアセットを早期に検出できるようになります。ただし、Angular アプリにリソースヒントを含めることは、次の理由により複雑です。
手動追加: デベロッパーが
preload
リソースヒントを手動で追加することは困難です。Angular は、プロジェクト全体またはウェブサイトのすべてのルートに対して、1 つの共有 index.html ファイルを使用します。したがって、ドキュメントの<head>
はすべてのルートで(少なくともサービス提供時には)同じになります。<head>
にpreload
ヒントを追加すると、不要な場合でも、すべてのルートでリソースがプリロードされます。そのため、preload
のヒントを手動で追加することはおすすめしません。レンダリング中の自動追加: フレームワークを利用して CSR アプリでのレンダリング中にドキュメントのヘッダーにプリロード ヒントを追加しても、解決しません。レンダリングは JavaScript がダウンロードされて実行されてから行われるため、
<head>
のレンダリングが遅すぎてどの値も得られません。ディレクティブの最初のバージョンでは、
preconnect
ヒントとfetchpriority
ヒントの組み合わせを使用して、preload
の代わりにイメージに優先順位を付けます。ただし、Aurora は現在、Angular CLI チームと共同で、ビルド時のリソースヒントの自動挿入を有効にしています。最新情報をお待ちください。サーバーでの画像のサイズと形式の最適化
Angular アプリは一般的にクライアントサイドでレンダリングされるため、ファイル システム上の画像はリクエスト時に圧縮できず、そのまま提供されます。このため、画像を圧縮し、オンデマンドで WebP や AVIF などの最新の形式に変換するには、画像 CDN を使用することをおすすめします。
このディレクティブではイメージ CDN の使用は強制されていませんが、ディレクティブとともに使用することを強くおすすめします。組み込まれたローダにより、正しい構成オプションが使用されるようになります。
効果
次のデモは、Angular 画像ディレクティブが画像のパフォーマンスに与える影響を示しています。次の 2 つのウェブサイトを比較します。
ウェブサイト 1: Imgix CDN から配信される画像にネイティブ <img>
要素を使用します(デフォルトの設定オプション)。
ウェブサイト 2: すべての画像にイメージ ディレクティブを使用します。また、ディレクティブによってスローされた警告やエラーによって直接推奨される最適化も含まれます。
チームはパートナーと協力して、実際のエンタープライズ Angular アプリケーションに対するイメージ ディレクティブのパフォーマンスへの影響を検証しました。
そのパートナーの一つが「Land's End」です。同社のサイトが、実際のアプリケーションで得られる結果を確認するテストケースとして適していると期待されていました。
QA 環境で、イメージ ディレクティブの使用前と使用後に Lighthouse ラボテストを実施しました。パソコンでは、LCP の中央値は 12.0 秒から 3.0 秒に低下し、LCP が 75% 改善しました。モバイルでは、LCP の中央値が 20.2 秒から 12.0 秒に低下(40.6% 改善)しました。
今後のロードマップ
これは、Angular 画像ディレクティブ用のデザインの最初のパートです。今後のバージョンでは、他にも次のような多くの機能が計画されています。
レスポンシブ画像のサポートの強化:
現在、
NgOptimizedImage
ではsrcset
の使用がサポートされていますが、画像ごとにsrcset
属性とsizes
属性を手動で指定する必要があります。将来的には、このディレクティブでsrcset
属性とsizes
属性を自動的に生成できるようにする予定です。リソースヒントの自動挿入
Angular CLI と統合して、重要な LCP イメージの事前接続タグとプリロードタグを生成できる場合があります。
Angular SSR のサポート
MVP バージョンは Angular CSR の制約を念頭に置いて設計されていますが、Angular SSR(Angular/Universal)の画像最適化ソリューションを検討することも重要です。
デベロッパー エクスペリエンスの改善
NgOptimizedImage
では、画像ごとにwidth
属性とheight
属性を指定する必要があります。ただし、デベロッパーによっては、これらをイメージごとに指定するのは面倒な場合があります。次のイテレーションでは、以下のようにデベロッパー エクスペリエンスが向上する可能性があります。- 幅と高さを明示的に定義する必要がない追加のモード(Next.js の "
fill
" 画像レイアウト オプションに類似)をサポートします。 - CLI 統合を使用して、画像の実際のサイズを判断することで、ローカル画像の幅と高さを自動的に設定します。
- 幅と高さを明示的に定義する必要がない追加のモード(Next.js の "
まとめ
Angular イメージ ディレクティブは、v14.2.0 のデベロッパー プレビュー バージョン以降、段階的にデベロッパーが利用できるようになります。NgOptimizedImage
をお試しいただき、フィードバックをお寄せください。
協力してくれた Katie Hempenius と Alex Castle に感謝します。