効果的な画像コンポーネントの作成

画像コンポーネントには、パフォーマンスに関するベスト プラクティスが組み込まれており、画像を最適化するためのすぐに使えるソリューションが用意されています。

Leena Sohoni
Leena Sohoni
Kara Erickson
Kara Erickson
Alex Castle
Alex Castle

画像は、ウェブ アプリケーションのパフォーマンス ボトルネックの一般的な原因であり、最適化の重要な重点分野です。最適化されていない画像はページ肥大化の原因になり、現在は 90 パーセンタイルでページの合計サイズ(バイト単位)の 70% 以上を占めています。画像を最適化する複数の方法では、デフォルトで組み込まれたパフォーマンス ソリューションを備えたインテリジェントな「画像コンポーネント」が必要です。

Aurora チームは、Next.js と協力してそのようなコンポーネントを構築しました。目標は、ウェブ デベロッパーがさらにカスタマイズできるよう、最適化された画像テンプレートを作成することです。このコンポーネントは優れたモデルとして機能し、他のフレームワーク、コンテンツ マネジメント システム(CMS)、技術スタックで画像コンポーネントを構築するための標準となります。Google は、Nuxt.js 用の同様のコンポーネントを共同で開発しており、今後のバージョンでは Angular と連携して画像の最適化に取り組んでいます。この投稿では、Next.js Image コンポーネントの設計方法と、その過程で学んだ教訓を紹介します。

画像の拡張機能としての画像コンポーネント

画像の最適化に関する問題と最適化案

画像はパフォーマンスだけでなく、ビジネスにも影響します。ウェブサイトにアクセスするユーザーのコンバージョン数に最も大きく寄与した予測要素は、ページ上の画像の数でした。ユーザーがコンバージョンに至ったセッションでは、コンバージョンに至らなかったセッションと比べて画像が 38% 少なかった。Lighthouse では、ベスト プラクティスの監査の一環として、画像の最適化とウェブに関する指標の改善に役立つ最適化案が複数挙げられています。画像がウェブに関する主な指標とユーザー エクスペリエンスに影響する一般的な領域には、次のようなものがあります。

サイズ調整されていない画像による CLS の低下

サイズを指定せずに画像を配信すると、レイアウトが不安定になり、Cumulative Layout Shift(CLS)が増加する可能性があります。img 要素に width 属性と height 属性を設定すると、レイアウト シフトを防止できます。次に例を示します。

<img src="flower.jpg" width="360" height="240">

幅と高さは、レンダリングされた画像のアスペクト比が自然なアスペクト比に近づけるように設定する必要があります。アスペクト比が大きく異なると、画像がゆがんで見えることがあります。CSS でアスペクト比を指定できる比較的新しいプロパティを使用すると、CLS を回避しながら画像のサイズをレスポンシブに変更できます。

大きい画像は LCP に悪影響を及ぼす可能性がある

画像のファイルサイズが大きいほど、ダウンロードに時間がかかります。大きな画像は、ページの「ヒーロー」画像や、Largest Contentful Paint(LCP)をトリガーするビューポート内の最も重要な要素です。重要なコンテンツの一部である画像のダウンロードに時間がかかると、LCP が遅れます。

多くの場合、デベロッパーは圧縮率の向上とレスポンシブな画像の使用により、画像サイズを縮小できます。<img> 要素の srcset 属性と sizes 属性を使用すると、さまざまなサイズの画像ファイルを指定できます。これにより、ブラウザは画面のサイズと解像度に応じて適切なオプションを選択します。

不適切な画像圧縮は LCP に悪影響を及ぼす可能性がある

AVIFWebP などの最新の画像形式は、一般的に使用されている JPEG 形式や PNG 形式よりも圧縮率が優れています。圧縮率を高めると、同じ画質の画像でも、ファイルサイズが 25 ~ 50% 小さくなる場合があります。この削減により、データ使用量を抑えながら、より高速にダウンロードできるようになります。アプリは、これらの形式をサポートするブラウザに最新の画像形式を配信する必要があります。

不要な画像の読み込みによる LCP への影響

スクロールしなければ見えない範囲の画像やビューポートにない画像は、ページの読み込み時にユーザーに表示されません。LCP に影響が及ばないように、LCP を遅延させることができます。遅延読み込みを使用すると、後からユーザーが画像に向かってスクロールしたときに、画像を読み込むことができます。

最適化の課題

前述の問題によるパフォーマンス コストを評価し、それらを克服するためのベスト プラクティスのソリューションを実装できます。しかし、そのようなことは実際には起こらないことが多く、非効率な画像によってウェブの速度が低下し続けています。これには次の理由が考えられます。

  • 優先事項: ウェブ デベロッパーは通常、コード、JavaScript、データの最適化に注力する傾向があります。そのため、画像の問題や最適化方法を知らない可能性があります。デザイナーが作成した画像やユーザーがアップロードした画像は、優先度の高いものから上位にならない場合があります。
  • すぐに使えるソリューション: デベロッパーが画像最適化の微妙な違いを認識している場合でも、フレームワークや技術スタックにすぐに使えるオールインワンのソリューションがないことが抑止力となる可能性があります。
  • 動的画像: アプリケーションの一部である静止画像に加えて、動的画像がユーザーによってアップロードされるか、外部データベースや CMS から取得されます。画像のソースが動的である場合、このような画像のサイズを定義することは困難な場合があります。
  • マークアップのオーバーロード: 画像サイズやさまざまなサイズの srcset を指定するソリューションでは、画像ごとに追加のマークアップが必要であり、面倒な作業になることがあります。srcset 属性は 2014 年に導入されましたが、現在ではウェブサイトのうち 26.5%しか使用されていませんsrcset を使用する場合、デベロッパーはさまざまなサイズの画像を作成する必要があります。just-gimme-an-img などのツールも有効ですが、すべての画像に対して手動で使用する必要があります。
  • ブラウザのサポート: AVIF や WebP などの最新の画像形式では、作成される画像ファイルのサイズが小さくなりますが、対応していないブラウザの場合は特別な処理が必要になります。デベロッパーは、画像がすべてのブラウザに配信されるように、コンテンツ ネゴシエーション<picture> 要素などの戦略を使用する必要があります。
  • 遅延読み込みのウォッチフェイスの追加機能: スクロールしなければ見えない範囲の画像に遅延読み込みを実装するには、複数の手法とライブラリを利用できます。最適なものを選ぶのは簡単ではありません。デベロッパーは、遅延画像を読み込むための「折りたたみ状態」から最適な距離がわからない場合もあります。デバイスでビューポートのサイズが異なると、状況がさらに複雑になる可能性があります。
  • 状況の変化: パフォーマンス向上のためにブラウザが新しい HTML 機能や CSS 機能に対応するようになると、デベロッパーがそれぞれの機能を評価することが難しくなる場合があります。たとえば、Chrome では取得優先度機能をオリジン トライアルとして導入しています。ページ上の特定の画像の優先度を上げるために使用できます。全体として、このような機能強化をコンポーネント レベルで評価、実装した方が、デベロッパーにとって使いやすいでしょう。

ソリューションとしての画像コンポーネント

画像を最適化する機会があり、アプリケーションごとに画像を個別に実装するのは難しいことから、私たちは画像コンポーネントのアイデアに行きつきました。イメージ コンポーネントはベスト プラクティスをカプセル化して適用できます。<img> 要素を画像コンポーネントに置き換えることで、デベロッパーは画像のパフォーマンスの問題に適切に対処できます。

Google は昨年、Next.js フレームワークと連携して、その Image コンポーネントを設計し、implementしてきました。次のように、Next.js アプリの既存の <img> 要素の置き換えとして使用できます。

// Before with <img> element:
function Logo() {
  return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}

// After with image component:
import Image from 'next/image'

function Logo() {
  return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}

このコンポーネントは、豊富な機能と原則を使用して画像関連の問題全般に対処します。また、デベロッパーがさまざまな画像要件に合わせてカスタマイズできるオプションも用意されています。

レイアウト シフトからの保護

前述のように、サイズ調整されていない画像はレイアウト シフトを引き起こし、CLS に影響します。Next.js Image コンポーネントを使用する場合、デベロッパーはレイアウト シフトを防ぐために、width 属性と height 属性を使用して画像サイズを指定する必要があります。サイズが不明な場合、デベロッパーは layout=fill を指定して、サイズ調整されたコンテナ内にあるサイズ調整されていない画像を提供する必要があります。静的イメージのインポートを使用して、ビルド時にハードドライブ上の実際のイメージのサイズを取得し、イメージに含めることもできます。

// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />

// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />

// Image component with image import
import Image from 'next/image'
import logo from './logo.png'

function Logo() {
  return <Image src={logo} alt="logo" />
}

デベロッパーはサイズ指定のない Image コンポーネントを使用できないため、時間をかけて画像のサイズ設定を検討し、レイアウトの移動を防ぐようにしています。

迅速な対応を促進

デバイスが変わっても画像をレスポンシブにするには、<img> 要素で srcset 属性と sizes 属性を設定する必要があります。Image コンポーネントを使用することで、この労力を軽減したいと考えました。Next.js Image コンポーネントは、アプリケーションごとに 1 回だけ属性値を設定するように設計されています。レイアウト モードに基づいて、Image コンポーネントのすべてのインスタンスに適用されます。3 つのパートからなるソリューションを作成しました。

  1. deviceSizes プロパティ: このプロパティを使用すると、アプリのユーザーベースに共通するデバイスに基づいて 1 回だけブレークポイントを設定できます。ブレークポイントのデフォルト値は構成ファイルに含まれています。
  2. imageSizes プロパティ: これも、デバイスサイズのブレークポイントに対応する画像サイズを取得するために使用する設定可能なプロパティです。
  3. 各画像の layout 属性: 各画像の deviceSizes プロパティと imageSizes プロパティの使用方法を示すために使用します。レイアウト モードでサポートされている値は fixedfillintrinsicresponsive です

レイアウト モードが response または fill である画像がリクエストされると、Next.js はページをリクエストしているデバイスのサイズに基づいて表示する画像を特定し、画像に srcsetsizes を適切に設定します。

次の比較は、レイアウト モードを使用してさまざまな画面の画像サイズを制御する方法を示しています。Next.js ドキュメントで共有しているデモ画像をスマートフォンと標準的なノートパソコンで表示しました。

ノートパソコンの画面 電話による選抜
Layout = Intrinsic: 小さいビューポートではコンテナの幅に合わせてスケールダウンされます。大きいビューポートでは、画像の本来のサイズを超えてスケールアップされません。コンテナの幅を 100% にする
そのままの山の画像 山の画像が縮小されました
レイアウト = 修正済み: 画像がレスポンシブではありません。幅と高さは、レンダリングされるデバイスに関係なく、「」要素と同様に固定されます。
そのままの山の画像 そのままの山の画像が画面に収まりません
レイアウト = レスポンシブ: アスペクト比を維持しながら、さまざまなビューポートでコンテナの幅に応じてスケールダウンまたはスケールアップを行います。
山の画像が画面に合わせて拡大されました 画面に合わせて縮小された山の画像
レイアウト = 塗りつぶし: 親コンテナに合わせて幅と高さが引き伸ばされます。(親 `
この例では幅が 300*500 に設定されています)
300×500 のサイズに合わせてレンダリングされた山の画像 300×500 のサイズに合わせてレンダリングされた山の画像
さまざまなレイアウトでレンダリングされた画像

組み込みの遅延読み込みを提供する

Image コンポーネントには、パフォーマンスに優れた組み込みの遅延読み込みソリューションがデフォルトで用意されています。<img> 要素を使用する場合、遅延読み込みのネイティブ オプションがいくつかありますが、すべてに使用しにくい欠点があります。デベロッパーは、次のいずれかの遅延読み込みのアプローチを採用できます。

  • loading 属性を指定する: この属性は簡単に実装できますが、現時点では一部のブラウザではサポートされていません
  • Intersection Observer API を使用する: カスタムの遅延読み込みソリューションを作成するには、労力と、入念な設計と実装が必要です。デベロッパーに必ずしもそうする時間があるとは限りません。
  • サードパーティ ライブラリをインポートして画像の遅延読み込みを行う: 遅延読み込みに適したサードパーティ ライブラリを評価して統合するには、追加の作業が必要になる場合があります。

Next.js Image コンポーネントでは、読み込みはデフォルトで "lazy" に設定されています。遅延読み込みは、最新のブラウザで利用可能な Intersection Observer を使用して実装します。デベロッパーはこの機能を有効にするために特別なことをする必要はありませんが、必要に応じて無効にできます。

重要な画像をプリロードする

多くの場合、LCP 要素は画像であり、画像が大きいと LCP が遅れる可能性があります。重要な画像をプリロードすると、ブラウザがその画像をより早く見つけられるようになります。<img> 要素を使用する場合、次のようにプリロード ヒントを HTML head に追加できます。

<link rel="preload" as="image" href="important.png">

画像コンポーネントを適切に設計すれば、使用するフレームワークに関係なく、画像の読み込み順序を微調整できるようになります。Next.js Image コンポーネントの場合、デベロッパーは images コンポーネントの priority 属性を使用して、プリロードに適した候補画像を指定できます。

<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />

priority 属性を追加すると、マークアップが簡素化され、使いやすくなります。画像コンポーネントのデベロッパーは、ヒューリスティックを適用して、特定の条件を満たすページ上のスクロールせずに見える画像のプリロードを自動化することもできます。

高パフォーマンスの画像ホスティングを促進する

画像 CDN は画像最適化の自動化に推奨され、WebP や AVIF などの最新の画像形式もサポートしています。Next.js Image コンポーネントは、デフォルトでローダ アーキテクチャを使用した画像 CDN を使用します。次の例は、ローダーによって Next.js 構成ファイルで CDN の構成が許可されていることを示しています。

module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://ImgApp/imgix.net',
  },
}

この設定では、デベロッパーは画像ソースで相対 URL を使用でき、フレームワークは相対 URL を CDN パスに連結して絶対 URL を生成します。ImgixCloudinaryAkamai などの一般的な画像 CDN がサポートされています。このアーキテクチャでは、アプリにカスタム loader 関数を実装することで、カスタム クラウド プロバイダの使用をサポートしています。

自己ホスト型画像をサポートする

ウェブサイトで画像 CDN を使用できない場合があります。そのような場合、画像コンポーネントでは自己ホスト画像をサポートする必要があります。Next.js Image コンポーネントは、CDN のような API を提供する組み込み画像サーバーとして画像オプティマイザーを使用します。オプティマイザーがサーバーにインストールされている場合、Sharp を使用して本番画像の変換を行います。このライブラリは、独自の画像最適化パイプラインを構築したい場合に適しています。

プログレッシブ読み込みをサポートする

プログレッシブ読み込みとは、実際の画像の読み込み中に通常かなり低品質のプレースホルダ画像を表示することで、ユーザーの関心を引くために使用される技術です。知覚パフォーマンスを向上させ、ユーザー エクスペリエンスを向上させる。スクロールしなければ見えない範囲の画像またはスクロールせずに見える範囲の画像の遅延読み込みと組み合わせて使用できます。

Next.js Image コンポーネントは、placeholder プロパティによる画像のプログレッシブ読み込みをサポートしています。これを LQIP(低画質画像のプレースホルダ)として使用すると、実際の画像が読み込み中に低画質の画像やぼやけた画像を表示できます。

効果

上記のすべての最適化が組み込まれた結果、本番環境での Next.js Image コンポーネントでの成功が確認されています。また、同様の画像コンポーネントに関して、他の技術スタックとも連携しています。

Leboncoin は、以前の JavaScript フロントエンドを Next.js に移行する際に、Next.js Image コンポーネントを使用するように画像パイプラインもアップグレードしました。<img> から next/image に移行したページで LCP が 2.4 秒から 1.7 秒に低下しました。このページでダウンロードされた画像の合計バイト数は 663 KB から 326 KB になりました(遅延読み込みされた画像バイト数は約 100 KB)。

得られた教訓

Next.js アプリを作成する場合、Next.js Image コンポーネントを使用して最適化するとメリットが得られます。ただし、別のフレームワークや CMS 向けに同様のパフォーマンスの抽象化を作成したい場合は、その過程で学んだ次の教訓が役立つ可能性があります。

安全弁は悪影響より悪影響が大きい

Next.js Image コンポーネントの初期リリースでは、デベロッパーがサイズの要件を回避し、サイズが指定されていない画像を使用できるように、unsized 属性を提供しました。これは、画像の高さや幅を事前に把握することが不可能な場合に必要だと考えました。しかし、CLS を悪化させない方法で問題を解決できる場合でも、サイジング要件の問題に対する汎用的な解決策として GitHub の問題の unsized 属性を推奨していることがわかりました。その後、unsized 属性のサポートを終了し、削除しました。

有益な煩わしさと無意味な煩わしさを区別する

画像サイズの要件は「煩わしさ」の一例です。コンポーネントの使用は制限されますが、その代わり、大きなパフォーマンス上のメリットがあります。パフォーマンス上のメリットが明確にわかれば、ユーザーは制約を受け入れやすくなります。そのため、このトレードオフについては、コンポーネントに関するドキュメントやその他の公開資料で説明することをおすすめします。

ただし、パフォーマンスを犠牲にすることなく、このような摩擦に対する回避策を見つけることができます。たとえば、Next.js Image コンポーネントの開発中に、ローカルに保存されている画像のサイズを検索すると面倒だという苦情が寄せられました。静的画像のインポートを追加しました。これにより、Babel プラグインを使用してビルド時にローカル画像のサイズを自動的に取得できるため、このプロセスが効率化されます。

便利な機能とパフォーマンス最適化のバランスを取る

画像コンポーネントがユーザーに「役立つ摩擦」を課すだけの場合、デベロッパーはその画像コンポーネントを使用したくない傾向があります。しかし、画像のサイズ調整や srcset 値の自動生成といったパフォーマンス機能が最も重要であることがわかりました。自動遅延読み込みや組み込みの不鮮明なプレースホルダといったデベロッパー向けの便利な機能も Next.js Image コンポーネントへの関心が高まりました。

導入を促進する機能のロードマップを策定する

あらゆる状況で完璧に機能するソリューションを構築するのは、非常に困難です。75% の人にとってうまくいくものを設計し、残りの 25% の人には「このような場合、このコンポーネントは適していない」と伝えたくなるかもしれません。

実際には、この戦略はコンポーネント デザイナーとしての目標に反します。パフォーマンス上のメリットを得るために、開発者にコンポーネントを採用してもらいたい場合。移行できず、会話から取り残されたと感じているユーザーがいる状況では、このような対応は困難です。保護者はがっかりする可能性があり、否定的な認識が採用に影響を及ぼします。

長期的に見て、妥当なすべてのユースケースを網羅した、コンポーネントのロードマップを用意することをおすすめします。また、サポートされない内容とその理由をドキュメントに明記しておくと、そのコンポーネントが解決する問題について見通しを示すのに役立ちます。

おわりに

画像の使用と最適化は複雑です。デベロッパーは、優れたユーザー エクスペリエンスを確保しながら、画像のパフォーマンスと品質のバランスを取る必要があります。そのため、画像の最適化はコストと効果の大きな取り組みとなります。

各アプリを毎回作り直すのではなく、デベロッパー、フレームワーク、その他の技術スタックが独自の実装のリファレンスとして使用できるベスト プラクティス テンプレートを考案しました。他のフレームワークの画像コンポーネントもサポートしているため、この経験は確かに価値あるものになります。

Next.js の Image コンポーネントにより、Next.js アプリケーションのパフォーマンスが改善し、ユーザー エクスペリエンスが向上しました。Google は、このモデルが幅広いエコシステムでうまく機能する優れたモデルであると考えており、自社のプロジェクトにこのモデルを採用したいと考えているデベロッパーの声をお待ちしています。