投稿日:
更新日:

Next.js 13 でリリースされた機能の紹介

Authors

目次

はじめに

Next.js は、React ベースの Web アプリケーションフレームワークで、SSR(Server Side Rendering)等のフロントエンド開発に必要な機能を十分に備えています。 2022 年 10 月には、App Directory(App Router)というルーティングエンジンを beta 版として導入した Next.js 13 がリリースされました。

今回のブログでは、Next.js 13 の基本的な考え方と使い方について整理してみたいと思います。

Next.js

Next.js は、React を用いた Web アプリケーションフレームワークであり Vercel, Inc によって開発が進められています。 SSR(Server Side Rendering)やファイルシステムベースのルーティング(file-system based routing)、API の構築等、Web 開発に必要な機能をフルスタックで提供していることが特徴です。

TL;DR

公式ドキュメント:https://nextjs.org/docs/app

Next.js 13 では、ルーティングとレイアウトを改善した App Directory(beta 版)が導入されており、次の 4 つをサポートします。

  • Layouts
    • ステートを維持して再レンダリングを回避しつつ UI を簡単に共有できる
  • Server Component
    • 動的なアプリケーションのためにサーバファーストをデフォルトとする
  • Streaming
    • ロード状態を瞬時に表示して更新をストリーミングする
  • Suspense for Data Fetching
    • 新たな use フックによってコンポーネントレベルのフェッチを可能にする

また、Webpack の後継であり、Rust ベースで構築されている Turbopack と呼ばれるバンドラが alpha 版として導入されています。 Turbopack では、開発に必要な最小限のアセットのみをバンドルすることで従来と比較して約 700 倍の速さを実現しています。

さらに、レイアウトシフト無しで画像を簡単に表示するとともに、ファイルをオンデマンドで最適化してパフォーマンスを向上させられる Image コンポーネント、カスタムフォントを含むフォントの自動最適化や、プライバシとパフォーマンスを向上させるための外部ネットワークへのリクエストの削減に対応しています。

Next.js 13 でリリースされた機能

Next.13 では主に次の機能がリリースされます。(特に興味深い機能にのみフォーカスします)

App Directory(beta 版)

Next.js 公式ドキュメントに記載されている通り、App Directory を用いた Next.js プロジェクトは以下のコマンドで作成できます。

ディレクトリ

$ npx create-next-app@latest

これを実行すると、app ディレクトリ配下に以下のようなファイルが生成されます。

app
├── api
│   └── hello
│       └── route.ts # Route Handlers(API)(`/api/hello`)
├── favicon.ico
├── globals.css
├── layout.tsx # レイアウト(`/`)
├── page.module.css
└── page.tsx # ルーティング(`/`)

app ディレクトリは Next.js 12 における pages の上位互換となる存在です。

ただし、app ディレクトリでは、pages における getServerSidePropsgetStaticPropsgetInitialProps 関数はサポートされません。 Next.js 13 では、fetch 関数でハンドリングすることになるようです。

/**
 * Next.js 側で fetch 関数で取得したデータを自動でキャッシュ
 * → これを使って SSG が使える
 */
fetch('https://....')

/**
 * fetch 関数に cache: 'no-store' オプションを追加すると SSR が使える
 */
fetch('https://...', { cache: 'no-store' })

/**
 * fetch 関数に next: { revalidate: 10 } オプションを追加すると ISR が使える
 */
fetch('https://...', { next: { revalidate: 10 } })

ルーティング

Next.js 12 までは、pages ディレクトリ配下でルーティング構成を展開(Pages Router)しますが、Next.js 13 では、app ディレクトリ配下でルーティング構成を展開(App Router)します。 pages と同様に、app ディレクトリ配下のフォルダ名が URL の path となる、ファイルベースのルーティングが採用されています。

.
└── app
    └── profile
        └── detail # https://example.com/profile/detail となります

基本的な使い方は、pages ディレクトリと大差は無いと思います。 また、Next.js 13 で pages ディレクトリを引き続き使用することも可能です。

app ディレクトリ配下には、指定のファイルを作成することで様々なケースに対応することができます。

  • page.js(拡張子は jsjsxtsx が可能、以下同じ)
    • メインの表示内容を設定
  • layout.js
    • 複数のページに跨って利用できる共通レイアウトを設定
  • loading.js
    • ページ間遷移で表示するローディング画面を設定
  • error.js
    • エラーが発生した場合に自動的に回復を試みるコンポーネントに関するファイルの設定(エラー内容の表示等)
  • template.js
    • ページ間の遷移でアニメーションを設定
  • not-found.js
    • 存在しない URL に接続した場合に表示される画面を設定
  • head.js
    • head タグの情報を設定できる

例えば、以下のようなディレクトリ構成にしておくことで、profile 配下のレイアウトを共通化できます。

.
└── app
    └── profile
        ├── layout.js
        ├── overview
        │   └── page.js
        └── detail
            └── page.js

Turbopack(alpha 版)

Turbopack は、Rust で書かれたバンドラで、Webpack の後継となることが期待されています。 従来の WebPack バンドラと比較して、700 倍の速さを実現するみたいです。

Turbopack は、関数レベルのキャッシュリクエストレベルのコンパイル の二つの仕組みを組み合わせることで非常に効率的にバンドルを実行します。

以下、公式ドキュメントの内容を簡単にまとめます。

関数レベルのキャッシュ

Turbopack は関数レベルで漸増計算(incremental computation)を行なってプログラムをバンドルします。 漸増計算とは、ある入力データが更新されるたびに、更新されたデータに依存した出力のみを再計算することです。 これにより全てを再計算するよりも不必要な計算がなくなり、結果的により高速にバンドルできるようになります。

例えば、開発サーバで api.tssdk.ts をバンドルする場合を考えます。

turbopack-core-concepts-1.png

api.tssdk.ts をバンドルする場合、バンドラは api.tssdk.ts それぞれにおいてバンドルし、連結して一つのファイル(fullBundle)を構築します。 この時 Turbopack では、呼び出されたすべての関数の結果をキャッシュに保存します。

ここで、sdk.ts を追加開発して更新した場合、Turbopack はファイルシステムイベントを受け取り、再度バンドルする必要があることを認識します。

turbopack-core-concepts-2.png

Turbopack は新しい fullbundle を生成しますが、この時、更新の無い api.ts は再度バンドルするわけではなく、最初にバンドルした際のキャッシュを読み取り、concat に渡します。

以上のように、関数レベルのキャッシュを使用して不必要な再計算を削除することで、パフォーマンスが向上します。

Turbopack alpha 版では、キャッシュはホストマシンの RAM に保存されますが、将来的にはリモートキャッシュ(Vercel Remote Cache)にて管理されるようになるらしいです。

いずれにしても、大量にコンテンツが乗っかっている SSG(Static Site Generation)のビルドパフォーマンスは大幅に向上しそうな予感がします。

リクエストレベルのコンパイル

Next.js 11 以前では、開発サーバを起動する際に、アプリケーション全体をコンパイルしてから表示していましたが、パフォーマンスの観点から Next.js 11 からリクエストされたページのコードのみをコンパイルするようになりました。

一方で、ページ単位でのコンパイルでは、すべてのモジュールや view に隠されているページのコードも全て読み込みバンドルしてしまうため、Native ESM(ES Module) を使用した Vite のようなフレームワークと比べた場合は、実行速度で負けます。

そのため、Turbopack では、Native ESM のようにページのレンダリングに必要なコードのみを計算しブラウザに送信します。

Turbopack は、Vite と比較して、以下の特徴があります。

  • Native ESM を使用しない

    Vite は開発モードで Native ESM を使用し、コードをバンドルせずにブラウザに送信しますが、これが原因で大規模なアプリケーションではネットワークリクエストが連鎖的に大量発生し、起動時間が遅くなることがあります。 Turbopack はコードをバンドルしてから送信するため、ネットワークリクエストの連鎖を避け、特に大規模アプリケーションでのパフォーマンスが向上します。

  • esbuild を使用しない

    Vite は多くのタスクで esbuild を使用しますが、esbuild はキャッシュの活用が少ないため、再計算が多く発生します。 Rust ベースの Turbopack は積極的にキャッシュを活用し、無駄な計算を省くことで、大規模アプリケーションでのパフォーマンスを向上させます。

これらの点から、Turbopack におけるリクエストレベルのコンパイルは、特に大規模なアプリケーションにおいて、Vite よりも優れたパフォーマンスを発揮するとされています。

next/font(beta 版)

Next.js で、Font をホストできるようになりました。 これにより、フォント読み込みの最適化が期待できます。

import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function MyApp({ Component, pageProps }) {
  return (
    <main className={inter.className}>
      <Component {...pageProps} />
    </main>
  )
}

next/link

Next.js 13 からは、<Link> コンポーネント内に <a> タグを含める必要がなくなります。

import Link from 'next/link'

/**
* Next.js 12 での使用方法
*/
<Link href="/link">
  <a>リンク</a>
</Link>

/**
* Next.js 13 からの使用方法
*/
<Link href="/link">
  リンク
</Link>

また、Next.js 13.2 では、Statically Typed Links が beta 版で追加されています。 これは <Link> コンポーネントの href 属性において、存在するページへのリンクを静的に強制できます。

これまではタイプミス等で、存在しないページへのリンクを配置してしまうこともありましたが、この機能によりコンパイル時にエラーを検知することが可能になりました。

まとめ

今回のブログでは、Next.js 13 のざっくりとした変更点とリリース機能を紹介しました。

Next.js 13 は特に大規模なアプリケーション開発において、開発速度とパフォーマンスを大幅に向上させ、開発者にとってより利便性の高いフレームワークとなることが期待できます。

特に今回のリリースで追加された、App Router や Turbopack バンドラは、パフォーマンスを最適化する上で非常に興味深い仕様となっています。