🖼️

ゼロから学ぶ React, Next.js⑩【Learn Next.js】Chapter3

2024/05/25に公開

【Chapter3】フォントと画像の最適化

前の章では、Next.jsアプリケーションのスタイリング方法を学びました。カスタムフォントとヒーロー画像を追加することで、ホームページの作業を続けましょう。

この章で扱うトピック

  • 📝 next/fontを使ってカスタムフォントを追加する方法
  • 🖼️ next/imageを使って画像を追加する方法
  • ✅ Next.jsでフォントと画像がどのように最適化されるか

なぜフォントを最適化するのか?

フォントはウェブサイトのデザインに重要な役割を果たしますが、フォントファイルをフェッチしてロードする必要がある場合、プロジェクトでカスタムフォントを使用するとパフォーマンスに影響を与える可能性があります。

累積レイアウトずれ(CLS)は、ウェブサイトのパフォーマンスとユーザーエクスペリエンスを評価するためにGoogleが使用するメトリックです。フォントの場合、ブラウザが最初にテキストをフォールバックまたはシステムフォントでレンダリングし、カスタムフォントがロードされるとそれに置き換えられるときにレイアウトずれが発生します。このスワップにより、テキストのサイズ、間隔、またはレイアウトが変更され、周囲の要素がシフトする可能性があります。

ページの初期ロードに続いて、カスタムフォントのロードによるレイアウトずれを示すモックUI

next/fontモジュールを使用すると、Next.jsはアプリケーションのフォントを自動的に最適化します。ビルド時にフォントファイルをダウンロードし、他の静的アセットとともにホストします。つまり、ユーザーがアプリケーションにアクセスしたときに、パフォーマンスに影響を与えるようなフォントの追加のネットワークリクエストはありません。

クイズの時間です!
知識をテストし、学んだことを確認しましょう。

Next.jsはどのようにフォントを最適化しますか?

A. パフォーマンスを向上させる追加のネットワークリクエストを引き起こします。
B. すべてのカスタムフォントを無効にします。
C. 実行時にすべてのフォントをプリロードします。
D. フォントファイルを他の静的アセットとともにホストするので、追加のネットワークリクエストはありません。

解答

D. フォントファイルを他の静的アセットとともにホストするので、追加のネットワークリクエストはありません。
Next.jsはビルド時にフォントファイルをダウンロードし、他の静的アセットと一緒にホストします。つまり、ユーザーがアプリケーションにアクセスしたときに、パフォーマンスに影響するようなフォントの追加ネットワークリクエストは発生しません。


プライマリフォントの追加

これがどのように機能するかを確認するために、カスタムのGoogleフォントをアプリケーションに追加しましょう!

/app/uiフォルダに、fonts.tsという新しいファイルを作成します。このファイルは、アプリケーション全体で使用されるフォントを保持するために使用します。

next/font/googleモジュールからInterフォントをインポートします。これがプライマリフォントになります。次に、ロードするサブセットを指定します。この場合は'latin'です:

/app/ui/fonts.ts
+import { Inter } from 'next/font/google';
 
+export const inter = Inter({ subsets: ['latin'] });

最後に、/app/layout.tsx<body>要素にフォントを追加します:

/app/layout.tsx
import '@/app/ui/global.css';
+import { inter } from '@/app/ui/fonts';
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
+      <body className={`${inter.className} antialiased`}>{children}</body>
    </html>
  );
}

Inter<body>要素に追加することで、フォントはアプリケーション全体に適用されます。ここでは、フォントを滑らかにするTailwindのantialiasedクラスも追加しています。このクラスを使用する必要はありませんが、良いアクセントになります。

ブラウザに移動し、開発者ツールを開いてbody要素を選択します。スタイルの下にInterInter_Fallbackが適用されていることがわかります。


練習:セカンダリフォントの追加

アプリケーションの特定の要素にフォントを追加することもできます。

今度はあなたの番です!fonts.tsファイルで、Lusitanaというセカンダリフォントをインポートし、/app/page.tsxファイルの<p>要素に渡します。前と同様にサブセットを指定するだけでなく、フォントウェイトも指定する必要があります。

準備ができたら、以下のコードスニペットを展開して解答確認してください。

ヒント:

解答
/app/ui/fonts.ts
+import { Inter, Lusitana } from 'next/font/google';
 
export const inter = Inter({ subsets: ['latin'] });
 
+export const lusitana = Lusitana({
+  weight: ['400', '700'],
+  subsets: ['latin'],
+});
/app/page.tsx
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
+import { lusitana } from '@/app/ui/fonts';
 
export default function Page() {
  return (
    // ...
+    <p
+      className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`}
+    >
      <strong>Welcome to Acme.</strong> This is the example for the{' '}
      <a href="https://nextjs.org/learn/" className="text-blue-500">
        Next.js Learn Course
      </a>
      , brought to you by Vercel.
    </p>
    // ...
  );
}

最後に、<AcmeLogo />コンポーネントもLusitanaを使用しています。エラーを防ぐためにコメントアウトされていたので、コメントを外すことができます:

/app/page.tsx
// ...
 
export default function Page() {
  return (
    <main className="flex min-h-screen flex-col p-6">
      <div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
        <AcmeLogo />
        {/* ... */}
      </div>
    </main>
  );
}

以下のようにロゴが表示されるはずです:
Acmeロゴ

素晴らしい、アプリケーションに2つのカスタムフォントを追加しました!次に、ホームページにヒーロー画像を追加しましょう。


なぜ画像を最適化するのか?

Next.jsは、トップレベルの/publicフォルダの下にある画像などの静的アセットを提供できます。/public内のファイルは、アプリケーションで参照できます。

通常のHTMLでは、次のように画像を追加します:

<img
  src="/hero.png"
  alt="Screenshots of the dashboard project showing desktop version"
/>

しかし、これは以下のことを手動で行う必要があることを意味します:

  • 画像がさまざまな画面サイズでレスポンシブになるようにします。
  • さまざまなデバイス用に画像サイズを指定します。
  • 画像の読み込み時のレイアウトずれを防ぎます。
  • ユーザーのビューポートの外にある画像を遅延ロードします。

画像最適化は、Web開発における大きなトピックであり、それ自体が専門分野と見なされる可能性があります。これらの最適化を手動で実装する代わりに、next/imageコンポーネントを使用して画像を自動的に最適化できます。


<Image>コンポーネント

<Image>コンポーネントは、HTML<img>タグの拡張機能であり、以下のような自動画像最適化機能が付属しています:

  • 画像の読み込み時のレイアウトずれを自動的に防ぎます。
  • 大きな画像をビューポートが小さいデバイスに送信しないように、画像のサイズを変更します。
  • デフォルトで画像を遅延ロードします(画像はビューポートに入るときにロードされます)。
  • ブラウザがサポートしている場合は、WebPAVIFなどの最新の形式で画像を提供します。

デスクトップ版ヒーロー画像の追加

<Image>コンポーネントを使ってみましょう。/publicフォルダの中を見ると、hero-desktop.pnghero-mobile.pngの2つの画像があることがわかります。これら2つの画像は完全に異なるものであり、ユーザーのデバイスがデスクトップかモバイルかによって表示されます。

/app/page.tsxファイルで、next/imageからコンポーネントをインポートします。次に、コメントの下に画像を追加します:

/app/page.tsx
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
+ import Image from 'next/image';
 
export default function Page() {
  return (
    // ...
    <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
      {/* ここにヒーロー画像を追加 */}
+      <Image
+        src="/hero-desktop.png"
+        width={1000}
+        height={760}
+        className="hidden md:block"
+        alt="Screenshots of the dashboard project showing desktop version"
+      />
    </div>
    //...
  );
}

ここでは、width1000ピクセル、heirht760ピクセルに設定しています。レイアウトずれを避けるために画像の幅と高さを設定することをお勧めします。これらは、ソース画像と同じアスペクト比である必要があります。

また、モバイル画面でDOMから画像を削除するためのhiddenクラスと、デスクトップ画面で画像を表示するためのmd:blockクラスも確認できます。

メモ:mdブレークポイント

Tailwindでは、ユーティリティクラスにブレークポイントのプレフィックス(md,lgなど)を付けることで、特定の画面サイズでのみ適用されるようにできます。

md:blockを指定すると、mdブレークポイント(デフォルトでは画面幅768px)以上の画面サイズで要素をdisplay: block;にして表示します。

hidden md:blockという組み合わせは以下のように動作します:

  • 画面幅が768px未満(モバイルサイズ)の場合、hiddenクラスが適用され要素は非表示になります。md:blockクラスは無視されます。

  • 画面幅が768px以上(中型サイズ以上)の場合、hiddenクラスは上書きされ、md:blockクラスによって要素が表示されます。

これで、ホームページは次のようになっているはずです:

カスタムフォントとヒーロー画像を使用したスタイル付きのホームページ


練習:モバイル版ヒーロー画像の追加

今度はあなたの番です!追加したばかりの画像の下に、hero-mobile.png用に別の<Image>コンポーネントを追加してください。

  • 画像のwidth560ピクセル、heirht620ピクセルにしてください。
  • モバイル画面で表示され、デスクトップで非表示になるようにしてください。開発者ツールを使用して、デスクトップ画像とモバイル画像が正しく切り替わっているかどうかを確認できます。

準備ができたら、以下のコードスニペットを展開して解答確認してください。

解答
/app/page.tsx
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
 
export default function Page() {
  return (
    // ...
    <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
      {/* ここにヒーロー画像を追加 */}
      <Image
        src="/hero-desktop.png"
        width={1000}
        height={760}
        className="hidden md:block"
        alt="Screenshots of the dashboard project showing desktop version"
      />
      <Image
        src="/hero-mobile.png"
        width={560}
        height={620}
        className="block md:hidden"
        alt="Screenshot of the dashboard project showing mobile version"
      />
    </div>
    //...
  );
}

素晴らしい!あなたのホームページには、カスタムフォントとヒーロー画像が表示されるようになりました。

クイズの時間です!
知識をテストし、学んだことを確認しましょう。

正しいか誤りか:ディメンションのない画像とWebフォントは、レイアウトずれの一般的な原因です。

A. 正しい
B. 誤り

解答

A. 正しい
寸法のない画像やウェブフォントは、ブラウザが追加リソースをダウンロードする必要があるため、レイアウトずれの一般的な原因です。

おすすめの読み物

これらのトピックについては、リモート画像の最適化やローカルフォントファイルの使用など、さらに多くのことを学ぶことができます。フォントと画像の詳細について知りたい場合は、以下を参照してください:


次の章

https://zenn.dev/gunjo/articles/7d547c28e916de

Discussion