Next.jsのチュートリアルをやってみる【フォントと画像の最適化編】

Next.jsを学ぶために、Next.js公式のチュートリアルをやっていきます。
前回のチュートリアルはこちら。

Next.jsのチュートリアルをやってみる【CSS編】

初めからやる場合はこちらからどうぞ。

Next.jsのチュートリアルをやってみる【準備編】

今回学ぶこと

  • next.fontモジュールでカスタムフォントを追加する方法
  • next/imageモジュールで画像を追加する方法
  • Next.jsでフォントと画像を最適化する方法

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

カスタムフォントを使用すると、フォントファイルの取得や読み込みが必要になり、パフォーマンスに影響が出る可能性があるからです。

累積レイアウトシフトと呼ばれるウェブサイトのパフォーマンスとUXを評価するために使用するGoogleの指標があります。

フォントの場合レイアウトシフトは、ブラウザが最初にフォールバックフォントかシステムフォントでテキストをレンダリング、読み込み後にカスタムフォントに入れ替わった際に発生します。この入れ替えによってテキストのサイズ、間隔、レイアウトが変化して周囲の要素が移動することがあります。

Next.jsではnext/fontモジュールを使用すると、アプリケーション内のフォントを自動的に最適化します。ビルド時にフォントファイルをダウンロードして、ほかの静的アセットと一緒にホストするので、ユーザーがアクセスする際のパフォーマンスに影響するような追加ネットワークリクエストが発生しないのです。

プライマリフォントを追加する

アプリケーションにカスタムGoogleフォントを追加して、その動作を確認してみましょう。

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

/app/ui/fonts.ts
// Interフォントをインポート
import { Inter } from 'next/font/google';

// Interフォントのlatinサブセットを読み込む
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>
  );
}

<body>要素にInterを追加することで、アプリケーション全体にフォントを適用することができます。ここではフォントを滑らかにするためにTailwindのantialiasedクラスも追加しています。必須ではありませんが、覚えておくといいでしょう。

セカンダリーフォント

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

ではここで練習をしてみましょう。font.tsファイルでLusitanaというセカンダリーフォントをインポートし、/app/page.tsxファイルの<p>要素に渡してみてください。先ほどと同じようにサブセットを指定して、さらにフォントのウェイトも指定する必要があります。

  • ウェイトオプションが不明な場合は、コードエディタでTypeScriptのエラーを確認しましょう。
  • Google Fontsにアクセスし、Lusitanaを検索して利用可能なオプションを確認してください。
  • Next.jsのドキュメントから複数のフォントの追加と、オプション一覧を確認してください
/app/ui/font.ts
// Interフォントをインポート
import { Inter, Lusitana } from 'next/font/google';

// Interフォントのlatinサブセットを読み込む
export const inter = Inter({ subsets: ['latin'] });

// Lusitanaフォントのlatinサブセットを読み込む
export const lusitana = Lusitana({ weight: '400', 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 styles from '@/app/ui/home.module.css';
import { lusitana } from './ui/fonts';

export default function Page() {
  return (
    // lusitanaのクラス名を追加
    <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>
  );
}

以下の様にロゴが表示され文章のフォントが変化したら正解です。

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

Next.jsは画像のような静的アセットをトップレベルの/publicフォルダの下に置くことができます。/public内のファイルはアプリケーションから参照することができます。

通常、HTMLではこのように画像ファイルを使用します。

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

しかし、この場合、次のような作業を手作業で行わなければなりません。

  • 画像がさまざまな画面サイズに対応するようにする。
  • さまざまなデバイス用に画像サイズを指定する。
  • 画像が読み込まれるときにレイアウトがずれないようにする。
  • ユーザーのビューポート外にある画像を遅延ロードする。

画像の最適化は、ウェブ開発における大きな話題であり、それ自体が1つの専門分野とも言えます。 これらの最適化を手動で実装する代わりに、next/imageコンポーネントを使用することで画像を自動的に最適化することができるのです。

<Image>コンポーネント

<Image>コンポーネントはHTMLの<img>タグを拡張したようなもので、次のような画像最適化機能を備えています。

  • 画像の読み込み時に自動的にレイアウトがずれるのを防ぐ
  • ビューポートが小さいデバイスに大きな画像を送らないように画像サイズを変更する
  • デフォルトで画像を遅延読み込みする
  • ブラウザがサポートしている場合、WebPAVIFのような最新フォーマットで画像を提供する

デスクトップ用画像の追加

<Image>コンポーネントを使用してみましょう。/publicフォルダの中を確認してみると、hero-desktop.pnghero-mobile.pngの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">
      {/* Add Hero Images Here */}
      <Image
        src="/hero-desktop.png"
        width={1000}
        height={760}
        className="hidden md:block"
        alt="Screenshots of the dashboard project showing desktop version"
      />
    </div>
    //...
  );
}

hiddenはモバイルでアクセスされたら表示されないように、md:blockはデスクトップで表示するように設定するためのクラスです。

モバイル用画像の追加

ではここでモバイル用の追加で練習してみましょう。

先ほど追加した画像の下に、hhero-mobile.png用の<image>コンポーネントをもう一つ追加します。画像の幅は560px、高さは620pxにしましょう。モバイル画面では表示され、デスクトップ画面では非表示になれば成功です。

ブラウザの開発者ツールでモバイル画面の確認ができますので、正しくなっているかを確認してみてください。

モバイル画面は以下の様になっているはずです。

まとめ

  • ユーザーがアクセスする際のパフォーマンスやずれに影響するのでフォントや画像を最適化する必要がある。
  • <body>タグにフォントを適用することで、アプリケーション全体にフォントを適用することができる。
  • Next.jsでは<Image>コンポーネントを使用して画像を読むと自動的に最適化される。

次回

Next.jsのチュートリアルをやってみる【レイアウトとページ編】

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です