画像コンポーネントはパフォーマンスのベスト プラクティスをカプセル化し、画像を最適化するためのすぐに使えるソリューションを提供します。
画像は、ウェブ アプリケーションのパフォーマンスのボトルネックの一般的な原因であり、最適化の重要な対象領域です。最適化されていない画像はページの肥大化につながるため、90 パーセンタイルでページの合計重量(バイト単位)の 70% 以上を占めています。画像を複数の方法で最適化するには、パフォーマンス ソリューションがデフォルトで組み込まれたインテリジェントな「画像コンポーネント」が必要です。
Aurora チームは Next.js と連携して、そのようなコンポーネントを 1 つ構築しました。ウェブ デベロッパーがさらにカスタマイズできる最適化された画像テンプレートを作成することが目標でした。このコンポーネントは優れたモデルとして機能し、他のフレームワーク、コンテンツ管理システム(CMS)、テクノロジー スタックで画像コンポーネントを構築するための標準を設定します。Google は、Nuxt.js のコンポーネントの開発に協力しており、今後のバージョンでは Angular と連携して画像の最適化に取り組んでいます。この記事では、Next.js Image コンポーネントの設計方法と、その過程で得た教訓について説明します。
画像の最適化に関する問題と最適化の機会
画像はパフォーマンスだけでなく、ビジネスにも影響します。ページ上の画像の数は、ウェブサイトにアクセスしたユーザーのコンバージョンの予測力で 2 番目に高い項目でした。ユーザーがコンバージョンに至ったセッションでは、コンバージョンに至らなかったセッションと比べて画像が 38% 少ないことがわかりました。Lighthouse のベスト プラクティス 監査では、画像を最適化してウェブに関する主な指標を改善するための複数の改善案が提示されます。画像が Core Web Vitals とユーザー エクスペリエンスに影響する可能性がある一般的な領域は次のとおりです。
サイズ指定されていない画像は CLS に悪影響を及ぼす
サイズが指定されていない画像を配信すると、レイアウトの不安定性が生じ、Cumulative Layout Shift(CLS)が高くなる可能性があります。<img>
要素に width
属性と height
属性を設定すると、レイアウト シフトを防ぐことができます。次に例を示します。
<img src="flower.jpg" width="360" height="240">
幅と高さは、レンダリングされた画像のアスペクト比が自然なアスペクト比に近くなるように設定する必要があります。アスペクト比の違いが大きい場合、画像が歪んで見えることがあります。CSS の aspect-ratio を指定できる比較的新しいプロパティを使用すると、CLS を防ぎながら画像のサイズをレスポンシブに調整できます。
大きな画像は LCP を低下させる可能性があります
画像のファイルサイズが大きいほど、ダウンロードに時間がかかります。大きな画像は、ページの「ヒーロー」画像や、Largest Contentful Paint(LCP)をトリガーするビューポート内の最も重要な要素である可能性があります。重要なコンテンツの一部であり、ダウンロードに時間がかかる画像は、LCP の遅延につながります。
多くの場合、デベロッパーは、より効果的な圧縮とレスポンシブ画像の使用によって画像サイズを削減できます。<img>
要素の srcset
属性と sizes
属性を使用すると、さまざまなサイズの画像ファイルを提供できます。ブラウザは、画面サイズと解像度に応じて適切な画像を選択できます。
画像の圧縮が不十分だと LCP が低下する
AVIF や WebP などの最新の画像形式は、一般的に使用されている JPEG や PNG 形式よりも圧縮率が高く、圧縮率を高めると、同じ画質でファイルサイズを 25% ~ 50% 削減できる場合があります。これにより、データ使用量を抑えながら、より高速にダウンロードできるようになります。アプリは、これらの形式をサポートするブラウザに最新の画像形式を配信する必要があります。
不要な画像の読み込みが LCP に悪影響を及ぼす
スクロールせずに見える範囲の下にある画像やビューポートに表示されない画像は、ページの読み込み時にユーザーに表示されません。遅延させることで、LCP に影響を与えず、遅延させることができます。遅延読み込みを使用すると、ユーザーがスクロールして画像が表示されたときに、その画像を読み込むことができます。
最適化の課題
チームは、前述の問題によるパフォーマンス コストを評価し、問題を克服するためのベスト プラクティス ソリューションを実装できます。しかし、実際にはそうはならないことが多く、非効率的な画像がウェブの速度低下を引き続き招いています。これには次の理由が考えられます。
- 優先順位: ウェブ デベロッパーは通常、コード、JavaScript、データの最適化に重点を置いています。そのため、画像に関する問題や画像の最適化方法を認識していない可能性があります。デザイナーが作成した画像やユーザーがアップロードした画像は、優先順位のリストでは上位にない場合もあります。
- すぐに使えるソリューション: デベロッパーが画像の最適化の微妙な違いを認識していても、フレームワークやテクノロジー スタックにすぐに使えるオールインワン ソリューションがない場合は、導入をためらう可能性があります。
- 動的画像: 動的画像は、アプリケーションの一部である静的画像に加えて、ユーザーがアップロードするか、外部データベースまたは CMS から取得します。画像のソースが動的である場合、そのような画像のサイズを定義するのは難しい場合があります。
- マークアップのオーバーロード: 画像サイズやサイズ別の
srcset
を含めるソリューションでは、画像ごとに追加のマークアップが必要になるため、手間がかかります。srcset
属性は 2014 年に導入されましたが、現在、ウェブサイトの 26.5%のみで使用されています。srcset
を使用する場合は、デベロッパーはさまざまなサイズの画像を作成する必要があります。just-gimme-an-img などのツールを使用することもできますが、画像ごとに手動で使用する必要があります。 - ブラウザのサポート: AVIF や WebP などの最新の画像形式では、作成される画像ファイルのサイズは小さくなりますが、対応していないブラウザでは特別な処理が必要になります。すべてのブラウザに画像を配信するには、コンテンツ ネゴシエーションや
<picture
> 要素などの戦略を使用する必要があります。 - 遅延読み込みのウォッチフェイスの追加機能: 折りたたみ式の画像の遅延読み込みを実装するには、複数の手法とライブラリを使用できます。最適なものを選ぶのは難しい場合があります。また、デベロッパーは、遅延読み込み画像を読み込むのに最適な「折り目」からの距離を把握していない可能性があります。デバイスによってビューポートのサイズが異なると、さらに複雑になります。
- 変化する状況: ブラウザがパフォーマンスを向上させる新しい HTML や CSS の機能をサポートし始めると、デベロッパーがそれらを個別に評価するのは難しくなる可能性があります。たとえば、Chrome ではオリジン トライアルとしてフェッチ優先度機能が導入されています。ページ上の特定の画像の優先度を上げるために使用できます。全体的に、このような機能強化はコンポーネント レベルで評価、実装するのがデベロッパーにとって簡単です。
ソリューションとしての画像コンポーネント
画像を最適化できる機会と、アプリごとに個別に実装する際の課題から、画像コンポーネントのアイデアが生まれました。画像コンポーネントは、ベスト プラクティスをカプセル化して適用できます。<img>
要素を画像コンポーネントに置き換えることで、デベロッパーは画像のパフォーマンスの問題をより適切に解決できます。
Google は昨年、Next.js フレームワークを使用して、画像コンポーネントを設計し、実装しました。次のように、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 つのパートからなるソリューションを考案しました。
deviceSizes
プロパティ: このプロパティを使用すると、アプリのユーザーベースに共通するデバイスに基づいて、ブレークポイントを 1 回だけ構成できます。ブレークポイントのデフォルト値は構成ファイルに含まれています。imageSizes
プロパティ: デバイスのサイズのブレークポイントに対応する画像サイズを取得するために使用される、構成可能なプロパティです。- 各画像の
layout
属性: 各画像のdeviceSizes
プロパティとimageSizes
プロパティの使用方法を指定します。レイアウト モードでサポートされている値は、fixed
、fill
、intrinsic
、responsive
です。
レイアウト モードが レスポンシブまたは フィットで画像がリクエストされると、Next.js はページをリクエストしたデバイスのサイズに基づいて配信する画像を特定し、画像の srcset
と sizes
を適切に設定します。
次の比較は、レイアウト モードを使用して、さまざまな画面で画像のサイズを制御する方法を示しています。ここでは、Next.js のドキュメントで共有されているデモ画像を使用し、スマートフォンと標準のノートパソコンで表示しました。
ノートパソコンの画面 | 電話による選抜 |
---|---|
レイアウト = 組み込み: 小さいビューポートでは、コンテナの幅に合わせて縮小されます。ビューポートが大きい場合でも、画像の元のサイズを超えて拡大されません。コンテナの幅は 100% です | |
![]() |
![]() |
レイアウト = 固定: 画像がレスポンシブではありません。幅と高さは、レンダリングされるデバイスに関係なく、 |
|
![]() |
![]() |
レイアウト = レスポンシブ: アスペクト比を維持しながら、さまざまなビューポートのコンテナの幅に応じてスケールダウンまたはスケールアップします。 | |
![]() |
![]() |
レイアウト = フィット: 幅と高さが伸ばされ、親コンテナに収まるように調整されます。(この例では、親の <div> の幅は 300*500 に設定されています)
|
|
![]() |
![]() |
遅延読み込みを組み込む
Image コンポーネントには、デフォルトで高性能の遅延読み込みソリューションが組み込まれています。<img>
要素を使用する場合、遅延読み込みにはいくつかのオプションがありますが、どれもデメリットがあり、使いづらいものです。デベロッパーは、次のいずれかの遅延読み込みアプローチを採用できます。
loading
属性を指定します。これはすべての最新ブラウザでサポートされています。- Intersection Observer API を使用する: カスタムの遅延読み込みソリューションを構築するには、労力と慎重な設計と実装が必要です。デベロッパーは、この作業に時間を割けない場合があります。
- サードパーティ ライブラリをインポートして画像を遅延読み込みする: 遅延読み込みに適したサードパーティ ライブラリを評価して統合するには、追加の労力が必要になる場合があります。
Next.js Image コンポーネントでは、デフォルトで読み込みが "lazy"
に設定されています。遅延読み込みは Intersection Observer を使用して実装されます。これは、最新のほとんどのブラウザで利用可能です。デベロッパーは、この機能を有効にするために特別な操作を行う必要はありませんが、必要に応じて無効にできます。
重要な画像をプリロードする
LCP 要素は多くの場合画像であり、大きな画像は LCP の遅延につながる可能性があります。ブラウザがその画像をより早く検出できるように、重要な画像をプリロードすることをおすすめします。<img>
要素を使用する場合は、次のように HTML ヘッダーにプリロード ヒントを含めることができます。
<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 を生成します。Imgix、Cloudinary、Akamai などの一般的な画像 CDN がサポートされています。このアーキテクチャでは、アプリにカスタム loader
関数を実装することで、カスタム クラウド プロバイダの使用をサポートしています。
セルフホスト イメージをサポートする
ウェブサイトで画像 CDN を使用できない場合があります。このような場合は、イメージ コンポーネントがセルフホスト イメージをサポートしている必要があります。Next.js Image コンポーネントは、CDN のような API を提供する組み込みの画像サーバーとして画像オプティマイザーを使用します。オプティマイザは、サーバーにインストールされている場合、本番環境の画像変換に Sharp を使用します。このライブラリは、独自の画像最適化パイプラインを構築しようとしている場合に適しています。
プログレッシブ ローディングをサポートする
プログレッシブ ローディングは、実際の画像の読み込み中に、通常は画質が大幅に低いプレースホルダ画像を表示することで、ユーザーの関心を維持する手法です。パフォーマンスの認識を高め、ユーザー エクスペリエンスを向上させます。折り返しの下にある画像や折り返しの上にある画像に対して、遅延読み込みと組み合わせて使用できます。
Next.js Image コンポーネントは、プレースホルダ プロパティを使用して画像の段階的な読み込みをサポートしています。これは 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 に同様のパフォーマンス抽象化を構築する場合は、Google が学んだ次の教訓が役に立つ可能性があります。
安全弁は、メリットよりもデメリットが大きい場合があります
Next.js Image コンポーネントの初期リリースでは、デベロッパーがサイズ要件を回避し、指定されていないサイズの画像を使用できるようにする unsized
属性が用意されていました。これは、画像の高さや幅を事前に把握できない場合に必要と判断されました。ただし、サイズ要件に関する問題の包括的な解決策として、GitHub の問題で unsized
属性が推奨されていることがわかりました。CLS を悪化させずに問題を解決できる場合でも、unsized
属性が推奨されています。その後、unsized
属性を非推奨にして削除しました。
有用な摩擦と無意味な煩わしさを区別する
画像のサイズ変更の要件は、「有用な摩擦」の例です。コンポーネントの使用は制限されますが、その代わりにパフォーマンスが大幅に向上します。ユーザーは、パフォーマンスの向上につながる可能性があることを明確に理解していれば、制約を受け入れやすくなります。したがって、コンポーネントに関するドキュメントやその他の公開資料で、このトレードオフについて説明することをおすすめします。
ただし、パフォーマンスを犠牲にすることなく、このような不便さを回避する方法はあります。たとえば、Next.js Image コンポーネントの開発中に、ローカルに保存されている画像のサイズを調べるのが面倒だという苦情が寄せられました。静的画像インポートを追加しました。これにより、Babel プラグインを使用してビルド時にローカル画像のディメンションを自動的に取得することで、このプロセスを効率化できます。
便利な機能とパフォーマンスの最適化のバランスを取る
画像コンポーネントがユーザーに「有用な摩擦」を与えるだけで何もしない場合は、デベロッパーがそのコンポーネントを使いたがらなくなる傾向があります。画像のサイズ調整や srcset
値の自動生成などのパフォーマンス機能が最も重要であることがわかりました。自動遅延読み込みやぼかしプレースホルダの組み込みなど、デベロッパー向けの便利な機能も、Next.js Image コンポーネントの注目を集めました。
機能のロードマップを設定して、導入を促進する
あらゆる状況で完璧に機能するソリューションを構築することは非常に困難です。75% のユーザーに適したコンポーネントを設計し、残りの 25% のユーザーには「このコンポーネントは適していません」と伝えたくなるかもしれません。
実際には、この戦略はコンポーネント デザイナーとしての目標と相反することになります。デベロッパーがコンポーネントを採用してパフォーマンスのメリットを享受できるようにします。移行できないユーザーがいて、会話から取り残されていると感じている場合は、この作業は困難です。失望を表明し、ネガティブな認識につながり、採用に影響する可能性があります。
長期にわたってすべての合理的なユースケースをカバーするコンポーネントのロードマップを用意することをおすすめします。また、コンポーネントが解決する問題について期待値を設定するために、サポートされていない内容とその理由をドキュメントで明記することも役立ちます。
まとめ
画像の使用と最適化は複雑です。デベロッパーは、優れたユーザー エクスペリエンスを確保しながら、画像のパフォーマンスと品質のバランスを取る必要があります。そのため、画像の最適化は費用と影響力の面で大きな取り組みとなります。
各アプリが毎回自力で開発するのではなく、デベロッパー、フレームワーク、その他のテクノロジー スタックが独自の実装のリファレンスとして使用できるベスト プラクティス テンプレートを作成しました。この経験は、他のフレームワークの画像コンポーネントをサポートする際に役立つでしょう。
Next.js Image コンポーネントにより、Next.js アプリケーションのパフォーマンスが改善され、ユーザー エクスペリエンスが向上しました。これは、幅広いエコシステムでうまく機能する優れたモデルであると考えています。このモデルをプロジェクトに採用したいとお考えのデベロッパーの方からのご意見をお待ちしております。