2022 年 5 月、Aurora チームと Angular チームは、Angular の画像ディレクティブの共同開発を発表しました。このディレクティブは、Angular v14.2 の一部としてデベロッパー プレビュー用に最近リリースされました。この投稿では、新しい画像ディレクティブ NgOptimizedImage
が Angular で画像の最適化をサポートする方法について説明します。
背景
画像はウェブ ユーザー エクスペリエンスの一般的な重要な要素であり、ウェブページの99.9% が 1 つ以上の画像のリクエストを生成しています。画像はページの重量に最も大きく影響し、1 ページあたりの中央値は 982 キロバイトです。
画像の数とサイズが増加しているため、ウェブページのパフォーマンスが低下し、ウェブに関する主な指標の指標に影響する可能性があります。2021 年には、デスクトップ ページの 79.4% で、画像が Largest Contentful Paint(LCP)要素でした。そのため、画像の最適化は多くのクリエイターにとって常に重要な課題となっています。
Aurora チームは、フレームワークの力を活用して、デベロッパーが直面する一般的な課題に組み込みのソリューションを提供することを信条としています。画像最適化分野への Google の最初の進出は、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 画像ディレクティブ(NgOptimizedImage
)がリリースされ、画像の最適化のデフォルトを Angular に設定できるようになりました。
機会
Angular は、現在デベロッパーが使用している主要な JavaScript フレームワークの 1 つです。これは、モバイルで HTTPArchive によってクロールされる 5 万を超えるオリジンで使用されており、NPM での週次ダウンロード数は 300 万回近くに上ります。
Core Web Vitals のスコアを確認すると、LCP の「良好」なしきい値を満たしている Angular オリジンの割合は、まだ改善の余地があります。2022 年 6 月時点で、Angular サイトのわずか 18.74% がモバイルで良好な LCP を達成しています。モバイルとパソコンのウェブページの 70% 以上で画像が LCP 要素であるため、最適化されていない LCP 画像が、Angular ウェブサイトでの LCP の低下の主な原因の一つである可能性があります。
Angular の画像ディレクティブは、これらの数値を改善することを目的としています。
NgOptimizedImage ディレクティブの MVP
Angular 画像ディレクティブの MVP は、Aurora がこれまでに構築した画像コンポーネントから得た教訓に基づいて構築され、Angular のクライアントサイド レンダリング エクスペリエンスに合わせて設計を調整しています。標準的な画像最適化の問題の多くは、次のいずれかによって解決されています。
- 強力なデフォルトを提供する。
- ベスト プラクティスに準拠するようにエラーまたは警告をスローする。
設計のハイライトは次のとおりです。
インテリジェントな遅延読み込み
ページの読み込み時にユーザーに表示されない画像(折り返しの下にある画像や非表示のカルーセル画像など)は、理想的には遅延読み込みする必要があります。遅延読み込みにより、ブラウザのリソースが解放され、他の重要なテキスト、メディア、スクリプトを読み込むことができます。ほとんどの画像は重要ではなく、遅延読み込みする必要がありますが、2021 年にはページの 7.8% のみがネイティブの遅延読み込みを使用していました。
Angular イメージ ディレクティブは、デフォルトで重要度の低い画像を遅延読み込みし、
priority
として特別にマークされた画像のみを即時読み込みします。これにより、ほとんどの画像で最適な読み込み動作が実現されます。重要な画像の優先順位付け
リソースヒントの追加(
preload
またはpreconnect
)を使用して、重要な画像の読み込みを優先することをおすすめします。ただし、ほとんどのアプリでは使用されていません。2021 年ウェブ年鑑によると、事前接続ヒントを使用しているモバイルページは12.7%、プリロード ヒントを使用しているモバイルページは22.1%に過ぎません。画像が優先としてマークされている場合、image ディレクティブは 2 つの面で機能します。
- 画像の fetchpriority を
"high"
に設定して、ブラウザが画像を優先度高くダウンロードする必要があることを認識できるようにします。 - 開発モードでは、ランタイム チェックによって、画像のオリジンに対応する
preconnect
リソース ヒントが含まれていることが確認されます。
開発モードでは、ディレクティブは PerformanceObserver API を使用して、LCP 画像が想定どおりに
priority
とマークされていることを確認します。priority
とマークされていない場合、エラーがスローされ、LCP イメージにpriority
属性を追加するようデベロッパーに指示されます。最終的には、この自動化とコンプライアンスの組み合わせにより、LCP イメージに
preconnect
ヒント、fetchpriority
属性値high
が付与され、遅延読み込みされなくなります。- 画像の fetchpriority を
一般的なイメージ ツール用に最適化された構成
Angular アプリケーションでは、画像 CDN を使用することをおすすめします。多くの場合、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 を自動的にフォーマットします。
組み込みのエラーと警告
このディレクティブには、上記の組み込み最適化に加えて、デベロッパーが画像マークアップで推奨されるベスト プラクティスに準拠していることを確認するためのチェックも組み込まれています。image ディレクティブは、次のチェックを実行します。
サイズ指定のない画像: 画像マークアップに明示的な幅と高さが定義されていない場合、image ディレクティブはエラーをスローします。サイズが指定されていない画像はレイアウト シフトを引き起こし、ページの累積レイアウト シフト(CLS)指標に影響する可能性があります。これを防ぐためのおすすめの方法は、画像に
width
属性とheight
属性を指定することです。アスペクト比: 画像ディレクティブは、HTML で定義された
width
:height
のアスペクト比がレンダリングされた画像の実際のアスペクト比に近くない場合に、エラーをスローしてデベロッパーに知らせます。このため、画面上の画像が歪んで見えることがあります。これは次のような場合に発生します。- 誤ってサイズ(幅または高さ)を定義している
- CSS で 1 つのディメンションをパーセンテージで定義し、もう 1 つのディメンションを定義していない場合(たとえば、
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>
は、(少なくともサービング時)すべてのルートで同じです。preload
ヒントを<head>
に追加すると、リソースが不要な場合でも、すべてのルートでリソースがプリロードされます。したがって、preload
ヒントを手動で追加することはおすすめしません。レンダリング中の自動追加: CSR アプリでレンダリング中にフレームワークを使用してドキュメントのヘッダーにプリロード ヒントを追加しても、効果はありません。レンダリングは JavaScript のダウンロードと実行後に行われるため、
<head>
は値が得られなくなるほど遅れてレンダリングされます。最初のバージョンのディレクティブでは、
preload
の代わりに、preconnect
ヒントとfetchpriority
ヒントを組み合わせて画像の優先順位付けを行います。ただし、Aurora は現在、Angular CLI チームと協力して、ビルド時にリソースヒントの自動挿入を可能にするための取り組みを進めています。今後の進展にご注目ください。サーバー上の画像サイズと形式の最適化
Angular アプリは通常、クライアントサイドでレンダリングされるため、ファイル システム上の画像はリクエスト時に圧縮できず、そのまま提供されます。このため、画像を圧縮してWebP などの最新形式や AVIF に変換するには、画像 CDN を使用することをおすすめします。
このディレクティブは画像 CDN の使用を強制するものではありませんが、ディレクティブとその組み込みローダで使用することを強くおすすめします。これにより、正しい構成オプションが使用されます。
影響
次のデモは、Angular の画像ディレクティブが画像のパフォーマンスに与える影響を示しています。2 つのウェブサイトを比較します。
ウェブサイト 1: ネイティブの <img>
要素を使用し、Imgix CDN を介して画像を提供します(デフォルトの構成オプションを使用)。
ウェブサイト 2: すべての画像に image ディレクティブを使用します。また、ディレクティブによってスローされた警告やエラーによって直接推奨される最適化も含まれます。
チームはパートナーと連携して、実際のエンタープライズ 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 の協力に感謝します。