2022 年 5 月、Aurora チームと Angular チームは、Angular 向けの画像ディレクティブに共同で取り組むことを発表しました。このディレクティブは、Angular v14.2 の一部として、デベロッパー プレビュー用に最近リリースされました。この投稿では、新しい画像ディレクティブ NgOptimizedImage
が Angular での画像最適化をサポートする方法について説明します。
背景
画像はウェブのユーザー エクスペリエンスに共通かつ重要なコンポーネントであり、ウェブページの 99.9% が 1 つ以上の画像のリクエストを生成します。また、画像はページのデータ量に最も大きく影響し、ページあたり 982 KB の中央値になります。
画像は数とサイズが増大するため、ウェブページのパフォーマンスを妨げ、Core Web Vitals の指標に影響を与える可能性があります。2021 年には、パソコン用ページの 79.4% で画像が 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 image コンポーネントは昨年リリースされました。現在、Angular 画像ディレクティブ(NgOptimizedImage
)がリリースされ、画像最適化のデフォルトが Angular になりました。
機会
Angular は、現在デベロッパーが使用している主要な JavaScript フレームワークの一つです。モバイルで HTTPArchive がクロールする 5 万以上のオリジンで使用され、NPM では毎週約 300 万件のダウンロードを記録しています。
Core Web Vitals のスコアで、Angular オリジンのうち「良好」LCP のしきい値については、まだ改善が必要です。2022 年 6 月時点で、モバイルでの LCP が良好だったのは Angular サイトの 18.74% のみでした。モバイルやパソコンのウェブページの 70% 以上では画像が LCP の要素となっているため、最適化されていない LCP 画像が、Angular ウェブサイトの LCP が低下する主な原因となっている可能性があります。
Angular 画像ディレクティブは、このような数値を改善するために設計されたものです。
NgOptimizedImage ディレクティブの MVP
Angular 画像ディレクティブの MVP は、Aurora がこれまでに構築した画像コンポーネントの教訓を基に、デザインを Angular のクライアント側レンダリング エクスペリエンスに適応させています。標準的な画像最適化の問題の多くは、次のいずれかの方法で対処されています。
- 強力なデフォルトを提供する。
- ベスト プラクティスへの準拠を徹底するために、エラーまたは警告をスローします。
主な設計は次のとおりです。
インテリジェントな遅延読み込み
ページの読み込み時にユーザーに表示されない画像(スクロールしなければ見えない範囲にある画像、非表示のカルーセル画像など)は、遅延読み込みが理想的です。遅延読み込みを使用すると、ブラウザ リソースが解放されて他の重要なテキスト、メディア、スクリプトを読み込むことができます。ほとんどの画像は重要ではなく、遅延読み込みすべきですが、2021 年にネイティブの遅延読み込みを使用しているのはページの 7.8% だけでした。
Angular 画像ディレクティブは、デフォルトで重要でない画像の遅延読み込みを行い、
priority
と指定された画像のみ積極的に読み込みます。これにより、ほとんどの画像で最適な読み込み動作を実現できます。重要なイメージの優先順位付け
リソースヒントの追加(例:
preload
やpreconnect
など)を使用して、重要な画像の読み込みを優先することが推奨されるベスト プラクティスです。ただし、ほとんどのアプリでは使用されていません。2021 Web Almanac によると、モバイルページの 12.7% のみが事前接続のヒントを使用し、プリロードのヒントを使用しているのはわずか 22.1% です。image ディレクティブは、画像が優先としてマークされている場合、2 つの前面で動作します。
- 画像の fetchPriority が
"high"
に設定され、ブラウザは優先度の高い画像をダウンロードする必要があります。 - 開発モードでは、ランタイム チェックによって、イメージのオリジンに対応する
preconnect
リソースヒントが含まれているかどうかが確認されます。
開発モードでは、このディレクティブは PerformanceObserver API も使用して、LCP イメージが想定どおりに
priority
とマークされていることを確認します。priority
とマークされていない場合は、エラーがスローされ、priority
属性を LCP 画像に追加するようデベロッパーに指示されます。最終的には、この自動化と適合性の組み合わせにより、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 に最適な構成の組み込みローダを提供します。これらのローダーは、画像の URL を自動的にフォーマットして、各 CDN で推奨される画像形式と圧縮設定が使用されるようにします。
組み込みのエラーと警告
このディレクティブには、上記の組み込み最適化に加えて、デベロッパーが画像マークアップで推奨されるベスト プラクティスに従っていることを確認するチェックも組み込まれています。image ディレクティブは、次のチェックを実行します。
サイズ指定されていない画像: 画像マークアップで幅と高さが明示的に定義されていない場合、image ディレクティブによってエラーがスローされます。画像のサイズを調整しないと、レイアウト シフトが発生し、ページの Cumulative Layout Shift(CLS)指標に影響する可能性があります。これを防ぐために推奨されるベスト プラクティスは、画像に
width
属性とheight
属性を指定することです。アスペクト比: 画像ディレクティブは、HTML で定義された
width
:height
のアスペクト比が、レンダリングされた画像の実際のアスペクト比と近くならない場合にデベロッパーに通知するエラーをスローします。これにより、画面の画像が歪んで見えることがあります。この問題は次の場合に発生する可能性があります。- 誤って間違ったサイズ(幅または高さ)を定義した
- 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 を使用しています。
画像ディレクティブは、CSR が Angular アプリの一般的なユースケースに合わせて完全にビルドされています。これには追加の制約があるため、チームは CSR アプリに特化した最適化を構築する方法を再考する必要がありました。
直面する課題には、以下のようなものがあります。
サポート リソースヒント
重要なアセットをプリロードすると、ブラウザが重要なアセットを早期に検出できるようになります。しかし、以下の理由により、Angular アプリにリソースヒントを含めることは複雑になります。
手動追加: デベロッパーが
preload
リソースヒントを手動で追加することは困難です。Angular では、プロジェクト全体またはウェブサイトのすべてのルートに対して、1 つの共有 index.html ファイルが使用されます。したがって、ドキュメントの<head>
はすべてのルートで同じです(少なくともサービス提供時)。preload
ヒントを<head>
に追加すると、不要な場所であっても、リソースがすべてのルートに対してプリロードされます。そのため、preload
ヒントを手動で追加することはおすすめしません。レンダリング中に自動的に追加: CSR アプリでのレンダリング中に、フレームワークを使用してプリロードのヒントをドキュメントのヘッダーに追加しても解決しません。レンダリングは JavaScript のダウンロードと実行の後に行われるため、
<head>
のレンダリングが遅すぎて、どの値もレンダリングできません。ディレクティブの最初のバージョンでは、
preconnect
ヒントとfetchpriority
ヒントを組み合わせることで、preload
の代わりに画像に優先順位を付けることができます。ただし、Aurora は現在 Angular CLI チームと連携して、ビルド時にリソースヒントを自動的に挿入できるように取り組んでいます。今しばらくお待ちください。サーバー上の画像のサイズと形式の最適化
Angular アプリは一般にクライアント側でレンダリングされるため、ファイル システム上の画像はリクエスト時に圧縮できず、そのまま提供されます。そのため、画像を圧縮し、WebP などの最新の形式や AVIF にオンデマンドで変換する場合は、画像 CDN を使用することをおすすめします。
このディレクティブでは、画像 CDN の使用が強制されることはありませんが、ディレクティブと一緒に使用することを強くおすすめします。このディレクティブの組み込みローダにより、正しい設定オプションが使用されるようになります。
影響
次のデモは、Angular 画像ディレクティブが画像のパフォーマンスにもたらす違いを示しています。2 つのウェブサイトを比較します。
ウェブサイト 1: Imgix CDN を介して配信される画像で、ネイティブの <img>
要素を使用します(デフォルトの構成オプションあり)。
ウェブサイト 2: すべての画像に画像ディレクティブを使用します。また、ディレクティブがスローする警告やエラーによって直接推奨される最適化も含まれています。
<ph type="x-smartling-placeholder">チームはパートナーと協力して、実際のエンタープライズ向け Angular アプリケーションに対する画像ディレクティブのパフォーマンスへの影響を検証しました。
その中の 1 社が Land's End です。実際のアプリケーションで得られる結果のテストケースとして、このサイトが良いテストケースであると考えていました。
image ディレクティブの使用前と使用後に、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 イメージの preconnect タグとプリロード タグを生成できる場合があります。
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 の協力に感謝します。