Angular Image ディレクティブを使用して画像を最適化する

Kara Erickson
Kara Erickson
Leena Sohoni
Leena Sohoni

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 万件のダウンロードを記録しています。

Angular ウェブサイトの LCP(過去 1 年間)。

Core Web Vitals のスコアから見て、LCP のしきい値が「良好」な Angular オリジンの割合を調べるには、まだ改善の余地があります。2022 年 6 月時点で、モバイルでの LCP が良好だったのは Angular サイトの 18.74% のみでした。モバイルやパソコンのウェブページの 70% 以上では画像が LCP の要素となっているため、最適化されていない LCP 画像が、Angular ウェブサイトの LCP が低下する主な原因となっている可能性があります。

Angular 画像ディレクティブは、このような数値を改善するために設計されたものです。

NgOptimizedImage ディレクティブの MVP

Angular 画像ディレクティブの MVP は、Aurora がこれまでに構築した画像コンポーネントの教訓を基に、デザインを Angular のクライアント側レンダリング エクスペリエンスに適応させています。標準的な画像最適化の問題の多くは、次のいずれかの方法で対処されています。

  • 強力なデフォルトを提供する。
  • ベスト プラクティスへの準拠を徹底するために、エラーまたは警告をスローします。

主な設計は次のとおりです。

  1. インテリジェントな遅延読み込み

    ページの読み込み時にユーザーに表示されない画像(スクロールしなければ見えない範囲にある画像、非表示のカルーセル画像など)は、遅延読み込みが理想的です。遅延読み込みを使用すると、ブラウザ リソースが解放されて他の重要なテキスト、メディア、スクリプトを読み込むことができます。ほとんどの画像は重要ではなく、遅延読み込みすべきですが、2021 年にネイティブの遅延読み込みを使用しているのはページの 7.8% だけでした。

    Angular 画像ディレクティブは、デフォルトで重要でない画像の遅延読み込みを行い、priority と指定された画像のみ積極的に読み込みます。これにより、ほとんどの画像で最適な読み込み動作を実現できます。

  2. 重要なイメージの優先順位付け

    リソースヒントの追加(例:preloadpreconnect など)を使用して、重要な画像の読み込みを優先することが推奨されるベスト プラクティスです。ただし、ほとんどのアプリでは使用されていません。2021 Web Almanac によると、モバイルページの 12.7% のみが事前接続のヒントを使用し、プリロードのヒントを使用しているのはわずか 22.1% です。

    image ディレクティブは、画像が優先としてマークされている場合、2 つの前面で動作します。

    • 画像の fetchPriority"high" に設定され、ブラウザは優先度の高い画像をダウンロードする必要があります。
    • 開発モードでは、ランタイム チェックによって、イメージのオリジンに対応する preconnect リソースヒントが含まれているかどうかが確認されます。

    開発モードでは、このディレクティブは PerformanceObserver API も使用して、LCP イメージが想定どおりに priority とマークされていることを確認します。priority とマークされていない場合は、エラーがスローされ、priority 属性を LCP 画像に追加するようデベロッパーに指示されます。

    最終的には、この自動化と適合性の組み合わせにより、LCP 画像に preconnect ヒントと fetchpriority 属性値 high が設定され、遅延読み込みが行われなくなります。

  3. 一般的な画像ツール用に最適化された構成

    Angular アプリケーションでは画像 CDN を使用することをおすすめします。多くの場合、デフォルトで最適化サービスが提供されます。

    このディレクティブは、画像 CDN の使用を促すため、アプリ内で特に魅力的なデベロッパー エクスペリエンス(DX)を提供し、設定で 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 で推奨される画像形式と圧縮設定が使用されるようにします。

  4. 組み込みのエラーと警告

    このディレクティブには、上記の組み込み最適化に加えて、デベロッパーが画像マークアップで推奨されるベスト プラクティスに従っていることを確認するチェックも組み込まれています。image ディレクティブは、次のチェックを実行します。

    1. サイズ指定されていない画像: 画像マークアップで幅と高さが明示的に定義されていない場合、image ディレクティブによってエラーがスローされます。画像のサイズを調整しないと、レイアウト シフトが発生し、ページの Cumulative Layout Shift(CLS)指標に影響する可能性があります。これを防ぐために推奨されるベスト プラクティスは、画像に width 属性と height 属性を指定することです。

    2. アスペクト比: 画像ディレクティブは、HTML で定義された width:height のアスペクト比が、レンダリングされた画像の実際のアスペクト比と近くならない場合にデベロッパーに通知するエラーをスローします。これにより、画面の画像が歪んで見えることがあります。この問題は次の場合に発生する可能性があります。

      1. 誤って間違ったサイズ(幅または高さ)を定義した
      2. CSS で一方の寸法を割合で定義したが、もう一方は定義していない場合(たとえば、width: 100% で画像を両方の寸法で拡大するには height: auto が必要です)。
    3. 画像のサイズが大きすぎる: 画像に srcset が定義されておらず、固有の画像がレンダリングされた画像よりも大幅に大きい場合、ディレクティブは srcset 属性と sizes 属性の使用を提案する警告を表示します。

    4. 画像密度: ピクセル密度が 3x を超える画像を srcset に含めると、このディレクティブによりエラーがスローされます。通常、2x よりも大きい記述子は推奨されません。高解像度のモバイル デバイスにサイズの大きな画像をダウンロードせざるを得なくなる可能性があるためです。さらに、人間の目は 2 倍を超える違いはほとんどわかりません

課題

NgOptimizedImage を設計する際の主な課題は、クライアントサイドのフレームワーク内で機能するように画像の最適化戦略を適応させることでした。Next.js のデフォルトのレンダリング エクスペリエンスはサーバーサイド レンダリング(SSR)または静的サイト生成(SSG)ですが、Angular ではクライアント サイド レンダリング(CSR)です。Angular は SSR ライブラリ(Angular/Universal)をサポートしていますが、ほとんどの Angular アプリ(約 60%)で CSR を使用しています。

画像ディレクティブは、CSR が Angular アプリの一般的なユースケースに合わせて完全にビルドされています。これには追加の制約があるため、チームは CSR アプリに特化した最適化を構築する方法を再考する必要がありました。

直面する課題には、以下のようなものがあります。

  1. サポート リソースヒント

    重要なアセットをプリロードすると、ブラウザで重要なアセットを早期に検出できるようになります。ただし、次のような理由により、リソースヒントを Angular アプリに含めることは複雑です。

    手動追加: デベロッパーが preload リソースヒントを手動で追加することは困難です。Angular は、プロジェクト全体またはウェブサイトのすべてのルートに対して、1 つの共有 index.html ファイルを使用します。したがって、ドキュメントの <head> はすべてのルートで同じです(少なくともサービス提供時)。preload ヒントを <head> に追加すると、不要な場所であっても、リソースがすべてのルートに対してプリロードされます。そのため、preload ヒントを手動で追加することはおすすめしません。

    レンダリング中に自動的に追加: CSR アプリでのレンダリング中に、フレームワークを使用してドキュメントのヘッダーにプリロードのヒントを追加しても、解決しません。レンダリングは JavaScript のダウンロードと実行の後に行われるため、<head> のレンダリングが遅すぎて、どの値もレンダリングできません。

    ディレクティブの最初のバージョンでは、preconnect ヒントと fetchpriority ヒントを組み合わせることで、preload の代わりに画像に優先順位を付けることができます。ただし、Aurora は現在 Angular CLI チームと連携して、ビルド時にリソースヒントを自動的に挿入できるように取り組んでいます。今しばらくお待ちください。

  2. サーバー上の画像のサイズと形式の最適化

    Angular アプリは一般にクライアント側でレンダリングされるため、ファイル システム上の画像はリクエスト時に圧縮できず、そのまま配信されます。そのため、画像を圧縮し、WebP や AVIF などの最新の形式にオンデマンドで変換する場合は、Image CDN を使用することをおすすめします。

    このディレクティブでは、画像 CDN の使用が強制されることはありませんが、ディレクティブと一緒に使用することを強くおすすめします。このディレクティブの組み込みローダにより、正しい設定オプションが使用されるようになります。

影響

次のデモは、Angular 画像ディレクティブが画像のパフォーマンスにもたらす違いを示しています。2 つのウェブサイトを比較します。

ウェブサイト 1: Imgix CDN を介して配信される画像で、ネイティブの <img> 要素を使用します(デフォルトの構成オプションあり)。

ウェブサイト 2: すべての画像に画像ディレクティブを使用します。また、ディレクティブがスローする警告やエラーによって直接推奨される最適化も含まれています。

フィルムストリップの比較: ネイティブ イメージタグを使用するウェブサイト 1 と、Angular 画像ディレクティブを使用するウェブサイト 2。

チームはパートナーと協力して、実際のエンタープライズ向け 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 属性を指定する必要があります。ただし、画像ごとにこれを指定するのは、一部のデベロッパーにとって面倒な作業です。次のイテレーションで、このデベロッパー エクスペリエンスを次のように改善できる可能性があります。

    1. 幅と高さの明示的な定義を必要としない追加のモード(Next.js の画像レイアウト オプション「fillに類似)をサポートします。
    2. CLI 統合を使用して、画像の実際のサイズを決定することにより、ローカル画像の幅と高さを自動的に設定します。

おわりに

Angular 画像ディレクティブは、v14.2.0 のデベロッパー プレビュー版から、段階的に提供されます。NgOptimizedImage をお試しいただき、ぜひフィードバックをお寄せください。

Katie Hempenius と Alex Castle の協力に感謝します。