🚀

Next.js 13.2まとめ

2023/02/25に公開

基本的には以下のNext.js 13.2のブログを翻訳してまとめたものになります。
https://nextjs.org/blog/next-13-2

TL;DR

  • ビルトインSEOサポート: 静的、動的にmetaタグを設定するための新しいMetadata API
  • ルートハンドラー: WebのRequestResponseに基づくカスタムリクエストハンドラー
  • MDXのサーバーコンポーネント対応: マークダウン内部でサーバーサイドのみで動くReactコンポーネントを使用
  • Rust製MDXパーサー: 全く新しいRust製プラグインによるマークダウンパースの高速化
  • エラーオーバーレイの改善: 可読性を改善するためにNext.jsとReactのスタックトレースを分離
  • 静的に型付けされたリンク(ベータ): next/linkとTypeScriptによる壊れたリンクの防止
  • Turbopackのいくつかの改善(アルファ): Webpackローダーとの互換性とサポートを改善
  • Next.jsキャッシュ(ベータ): 進歩的なISRとコード変更時の再デプロイの高速化

アップデート詳細

新しいMetadata APIによるビルトインSEOサポート

これまでappディレクトリではhead.jsmetaタグを記載していましたが、page.js、またはlayout.jsにNext.jsが定義したMetadataオブジェクトを記載する形式になりました。

Metadataオブジェクトが静的な場合はmetadata変数として、Metadataオブジェクトが動的な場合はgenerateMetadata関数としてexportすることでmeta情報を付与できます。

export const metadata = {
  title: '...',
  description: '...',
};

export async function generateMetadata() {
  ...
  return {
    title: '...',
    description: '...',
  };
};

また、head.jsはNested Layoutsに対応しておらず、全てのページで共通の内容も全てのhead.jsに書く必要がありましたが、Metadata APIはNested Layoutsに対応しており、Metadataはルートからマージされていきます。

詳細は以下のページで確認できます。
https://beta.nextjs.org/docs/api-reference/metadata

カスタムルートハンドラー

これまでappディレクトリではAPI Routes(pages/api)相当の機能がありませんでしたが、新たに追加されました。

page.jslayout.jsのようにroute.tsを定義して、大文字のHTTPメソッドを関数名とした関数としてexportすることで対応するHTTPメソッドのエンドポイントを作成できます。

app/route.ts
export async function GET(request: Request) { 
  ... 
};

ただし、同階層にpage.jsが存在する場合は使用できません。

詳細は以下のページで確認できます。
https://beta.nextjs.org/docs/routing/route-handlers

MDXのサーバーコンポーネント対応

MDXがサーバーコンポーネントに対応しました。

動的なUIをテンプレート化するReactの強力な機能を維持しながら、クライアントサイドJavaScriptを減らしてページロードを高速化できます。

@next/mdxプラグインがアップデートされ、カスタムコンポーネントを提供するためにアプリケーションのルートで定義される新しい特別なファイル、mdx-components.jsをサポートするようになりました。

mdx-components.js
// このファイルはMDXファイルで使用されるカスタムReactコンポーネントを提供します
// 他のライブラリを含む、あらゆるReactコンポーネントをimportして使用することができます
function H1({ children }) {
  ...
};

function H2({ children }) {
  ...
};

export function useMDXComponents(components) {
  return { h1: H1, h2: H2, ...components };
};

詳細は以下のページで確認できます。
https://beta.nextjs.org/docs/guides/mdx

Rust製MDXパーサー

MDXのサーバーコンポーネント対応の一環として、パフォーマンスを改善するためにMDXパーサーがRustで書き換えられ、大量のMDXファイルを処理する際に顕著な速度低下が見られた、以前のJavaScriptベースのパーサーと比較して大幅に改善されました。

next.config.jsでRustパーサーの使用をオプトインすることができます。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    mdxRs: true,
  },
};

const withMDX = require('@next/mdx')();
module.exports = withMDX(nextConfig);

エラーオーバーレイの改善

Next.jsとReactのスタックトレースが分離され、エラーの発生箇所を容易に特定することができるようになりました。さらに、エラーオーバーレイに現在のNext.jsのバージョンが表示されるようになり、バージョンが最新であるかどうかわかりやすくなりました。

静的に型付けされたリンク

next/linkを使用する際に、タイプミスなどのエラーを防ぐためにappディレクトリのリンクが静的に型付けされるようになりました。

next.config.jsで静的に型付けされたリンクの使用をオプトインすることができます。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    typedRoutes: true,
  },
};

module.exports = nextConfig;

詳細は以下のページで確認できます。
https://beta.nextjs.org/docs/configuring/typescript#statically-typed-links

Turbopackのいくつかの改善

Turbopackは現在アルファですがベータに向けて、Next.jsの既存機能をサポートし、全体的な安定性を改善することに注力してきたようです。

前回のリリースから以下が追加されています。

  • next/dynamicに対応
  • next.config.jsrewritesredirectsheaderspageExtensionsに対応
  • pagesの404とエラーに対応
  • CSS Modulesのcompose: ... from ...に対応
  • Fast Refreshの信頼性とエラーからの復旧の改善
  • CSSの優先順位の取扱いの改善
  • コンパイル時の評価の改善

Webpackのローダーを使用したカスタムファイル変換

TurbopackはいくつかのWebpackのローダーへのサポートと互換性を開始しました。これにより、様々な種類のファイルをJavaScriptへ変換するために、Webpackのエコシステムから多くのローダーを使用できるようになります。サポートされるローダーやTurbopackのカスタマイズの詳細は以下のページで確認できます。
https://turbo.build/pack/docs/features/customizing-turbopack

例えば、以下のようにnext.config.jsexperimental.turbo.loadersを使用して、各ファイルの拡張子に対するローダーのリストを設定します。

next.config.js
module.exports = {
  experimental: {
    turbo: {
      loaders: {
        '.md': [
          {
            loader: '@mdx-js/loader',
            options: {
              format: 'md',
            },
          },
        ],
        '.svg': ['@svgr/webpack'],
      },
    },
  },
};

ローダーを使用したTurbopackの完全な例は以下のページで確認できます。
https://github.com/vercel/next.js/tree/canary/examples/with-turbopack-loaders

Webpackスタイルのエイリアス解決

TurbopackはWebpackのresolve.aliasと同様に、エイリアスを使用してモジュール解決を行うように設定することができるようになりました。

例えば、以下のようにnext.config.jsexperimental.turbo.resolveAliasを使用して、設定します。

next.config.js
module.exports = {
  experimental: {
    turbo: {
      resolveAlias: {
        underscore: 'lodash',
        mocha: { browser: 'mocha/browser-entry.js' },
      },
    },
  },
};

Next.jsキャッシュ

ISRを進化させた新しいNext.jsキャッシュ(ベータ)が導入され、以下を実現します。

  • コンポーネントレベルの進歩的なISR
  • ネットワークリクエストなしでリフレッシュの高速化
  • 静的ページへのコード変更時の再デプロイの高速化

完全に静的なページについてはISRはこれまでと同じように動作します。静的と動的が混在する、よりきめ細やかなデータフェッチが必要なページについてはNext.jsキャッシュはよりきめ細やかで、短期間のキャッシュを使用します。

Reactのサーバーコンポーネントの基盤とNext.jsのappディレクトリでデータフェッチをコロケートする(一緒に書く)ことで、静的、または動的なデータをそれらを使用するコンポーネントのそばにカプセル化することができます。

app/page.jsx
export default async function Page() {
  const [staticData, dynamicData, revalidatedData] = await Promise.all([
    // 手動で無効にされるまでキャッシュされます
    fetch(`https://...`),
    // リクエストごとに再フェッチされます
    fetch(`https://...`, { cache: 'no-store' }),
    // 10秒間の有効期限でキャッシュされます
    fetch(`https://...`, { next: { revalidate: 10 } }),
  ]);

  return <div>...</div>;
}

appディレクトリでローカル開発している間、next devnext startを使用した本番環境と同様のキャッシュの振る舞いを確認できるようになります。これまでnext devではキャッシュの振る舞いは確認できなかったので、これは非常に便利ですね。

Next.jsキャッシュでは、サードパーティのAPIではなく、アプリがキャッシュを制御します。これはcache-controlヘッダーとは異なり、より上流で値がキャッシュされる期間を制御します。

Next.jsキャッシュによって、キャッシュの単位がページから一つ一つのデータになることで、よりきめ細やかなキャッシュの制御が可能になる一方で、難易度や複雑性は飛躍的に向上しそうですね。

詳細は以下のページで確認できます。
https://vercel.com/blog/vercel-cache-api-nextjs-cache

その他のいくつかの改善

  • フォント: @next/fontnext/fontとしてNext.jsにビルトインされ、@next/fontを別々にインストールする必要がなくなりました
  • パフォーマンス: エラーコンポーネントを最適化し、スタイルの変更なしでHTMLのペイロードを0.4KB削減しました
  • など

Discussion