🐿️

Next.js 13.4 全文和訳 と 補足説明

2023/05/17に公開

Written with ChatGPT-4


https://nextjs.org/blog/next-13-4

Next.js 13.4

2023年5月5日(金)

Next.js 13.4はApp Routerの安定性を示した、基礎的なリリースです。

  • App Router (安定版)
    • React Server Components
    • ネスト可能なルートとレイアウト
    • データ取得の簡素化
    • Streaming & Suspense
    • Built-in SEO サポート
  • Turbopack (ベータ版):ローカル開発サーバをより高速に、より安定的に。
  • Server Actions (アルファ版):クライアントJavaScriptを使用せずに、サーバー上でデータを変更できます。

Next.js 13のリリースから6か月間、我々は破壊的変更を最小限に抑えつつ、段階的に採用可能な形でNext.jsの未来の基盤を構築することに焦点を当ててきました。

本日、13.4がリリースされ、本番環境でのApp Routerの採用を開始することができるようになりました。

npm i next@latest react@latest react-dom@latest eslint-config-next@latest

Next.js App Router

我々は2016年にNext.jsをリリースし、Reactアプリケーションをサーバーレンダリングする簡単な方法を提供し、よりダイナミックでパーソナライズされたグローバルウェブの構築を目指してきました。

最初の発表投稿では、Next.jsの設計原則をいくつか共有しました:

  • セットアップ不要:ファイルシステムをAPIとして利用
  • JavaScriptのみ:すべてが関数
  • 自動的なサーバーレンダリングとコード分割
  • データの取得は開発者次第

Next.jsは現在6年目を迎えています。当初の設計原則はそのままに、Next.jsがより多くの開発者や企業に採用されるにつれて、これらの原則をより良く実現するためのフレームワークの基盤的なアップグレードに取り組んできました。

我々はNext.jsの次世代版に取り組んでおり、今日の13.4のリリースにより、この次世代版は安定して採用可能となりました。この記事では、App Routerの設計決定と選択について詳しく説明します。

セットアップ不要:ファイルシステムをAPIとして利用

ファイルシステムベースのルーティングはNext.jsのコア機能でした。初期の投稿では、1つのReactコンポーネントからルートを作成するこの例を示しました:

従来型のページルーター

//pages/about.js
import React from 'react';
export default () => <h1>私たちについて</h1>;

開発者は設定を追加する必要はありませんでした。ファイルをpages/ディレクトリに追加すれば、Next.jsのルーターが残りの部分を処理します。私たちは依然としてこのルーティングのシンプルさを愛しています。しかし、フレームワークの使用が増えるにつれて、開発者がそれを使って構築したいインターフェースの種類も増えてきました。

開発者たちは、レイアウトの定義、UIレイアウトのネスティング、ローディングとエラー状態の定義について、より柔軟なサポートを求めていました。これらの改善を既存のNext.jsルーターに後付けするのは容易なことではありませんでした。

フレームワークの各部分はルーターを中心に設計されるべきです。ページ遷移、データフェッチング、キャッシュ、データの変更と再検証、ストリーミング、コンテンツのスタイリングなどがそれに当たります。

ルーターをストリーミングに対応させ、レイアウトの強化されたサポートを提供するために、私たちは新しいバージョンのルーターの構築を決定しました。

これは私たちがレイアウトのRFCの初版リリース後に到達した結論です。

新 App Router ✨

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

// app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>;
}

ここで重要なのは、見ることができるものよりも、見ることができないものです。この新しいルーター(app/ディレクトリを通じて段階的に採用可能です)は、全く新しいアーキテクチャを持っており、React Server ComponentsSuspenseの基盤上に構築されています。

この新しい基盤により、我々は初めてReactのプリミティブを拡張するために開発されたNext.js特有のAPIを削除できました。例えば、全体的な共有レイアウトをカスタマイズするためにカスタム_appファイルを使用する必要はもうありません:

従来型のページルーター

// pages/_app.js
// この"global layout"は全てのルートをラップします。
// 他のレイアウトコンポーネントと組み合わせる方法はありませんし、
// このファイルからグローバルなデータをフェッチすることはできません。
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

この従来型のページルーターでは、レイアウトの組み合わせやコンポーネントと一緒にデータフェッチングを配置することができませんでしたが、新しいApp Routerではこれが可能になっています。

新 App Router ✨
// app/layout.js
//
// ルートレイアウトはアプリケーション全体で共有されます
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

// app/dashboard/layout.js
//
// レイアウトはネストや組み合わせが可能です
export default function DashboardLayout({ children }) {
  return (
    <section>
      <h1>Dashboard</h1>
      {children}
    </section>
  );
}

従来型のページルーターでは、_documentを使用してサーバーからの初期ペイロードをカスタマイズしていました。

従来型のページルーター

// pages/_document.js
// このファイルは、サーバーリクエストに対して<html>タグと<body>タグをカスタマイズすることを可能にします
// ただし、HTML要素を直接記述するのではなく、フレームワーク特有の機能が追加されます。
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

しかし、App Routerでは、Next.jsから<Html><Head><Body>をインポートする必要はもうありません。代わりに、直接Reactを使用します。

新 App Router ✨
// app/layout.js
// ルートレイアウトはアプリケーション全体で共有されます
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

新しいファイルシステムルーターの構築は、ルーティングシステムに関連する他の多くの機能要求に対応する絶好の機会でもありました。以下に例を示します。

  • 以前は、_app.jsにグローバルスタイルシートをインポートする際に、_external npm packages(コンポーネントライブラリなど)からのみ可能でした。これは、開発者体験としては理想的ではありませんでした。App Routerでは、任意のCSSファイルを任意のコンポーネントにインポート(および配置)することができます。
  • 以前は、Next.jsでサーバーサイドレンダリングを選択する(getServerSidePropsを通じて)と、全ページがハイドレートされるまでアプリケーションとの対話がブロックされてしまいました。しかし、App Routerでは、React Suspenseと深く統合されたアーキテクチャへとリファクタリングしました。これにより、ページの一部を選択的にハイドレートし、UIの他のコンポーネントがインタラクティブであることをブロックすることなく、コンテンツをサーバーから即座にストリームできます。これにより、ページの読み込み性能を向上させることができます。

ルーターは、Next.jsの機能を実現するコア部分です。しかし、ルーターそのものの重要性よりも、ルーターがフレームワークの残りの部分、たとえばデータフェッチといった要素をどのように統合しているかが重要です。

JavaScriptだけ。すべてが関数

Next.jsとReactの開発者は、JavaScriptとTypeScriptのコードを書き、アプリケーションのコンポーネントをまとめて使用したいと考えています。下記のコードは私たちの最初の投稿からの引用です。

import React from 'react';
import Head from 'next/head';

export default () => (
  <div>
    <Head>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
    </Head>
    <h1>Hi. I'm mobile-ready!</h1>
  </div>
);

このコンポーネントは、アプリケーション内のどこでも再利用し、組み合わせることができるロジックをカプセル化します。ファイルシステムルーティングと組み合わせると、JavaScriptとHTMLを書く感覚でReactアプリケーションを作り始める簡単な方法が得られました。

たとえば、データをフェッチしたい場合、Next.jsの最初のバージョンは次のようになります:

import React from 'react';
import 'isomorphic-fetch';

export default class extends React.Component {
  static async getInitialProps() {
    const res = await fetch('https://api.company.com/user/123');
    const data = await res.json();
    return { username: data.profile.username };
  }
}

しかし、フレームワークが成熟し、採用が拡大するにつれて、私たちは新しいデータフェッチングのパターンを探求しました。

getInitialPropsはサーバーとクライアントの両方で実行されました。このAPIはReactコンポーネントを拡張し、結果をコンポーネントのpropsに転送するPromiseを作成することを可能にしました。

getInitialPropsは今でも動作しますが、その後、顧客のフィードバックに基づいて次世代のデータフェッチングAPI、getServerSidePropsgetStaticPropsへと進化しました。

// ルートの静的バージョンを生成する
export async function getStaticProps(context) {
  return { props: {} };
}
// または、ルートを動的にサーバーサイドレンダリングする
export async function getServerSideProps(context) {
  return { props: {} };
}

これらのAPIによって、コードがクライアント側かサーバー側のどちらで実行されているかがより明確になり、Next.jsアプリケーションを自動的に静的に最適化することが可能になりました。さらに、静的なエクスポートが可能なため、サーバーをサポートしていない場所(AWS S3バケットなど)にNext.jsをデプロイできるようになりました。

しかし、これは "ただのJavaScript" ではありません。従って、私たちはより本来の設計思想に近いものを目指したいと考えました。

Next.jsが作成されて以来、私たちはMetaのReactコアチームと緊密に協力し、Reactの基本要素の上にフレームワークの機能を構築してきました。私たちのパートナーシップとReactコアチームによる長年の研究開発によって、Next.jsがReactアーキテクチャの最新バージョン、特にサーバーコンポーネントを含む形で目標達成の機会を得ることができました。

App Routerでは、おなじみのasyncawaitの構文を使用してデータをフェッチするため、新たに学ぶべきAPIはありません。デフォルトでは、すべてのコンポーネントはReact Server Componentsであるため、データフェッチングはサーバー上で安全に行われます。例えば、以下のような感じです。

app/page.js

export default async function Page() {
  const res = await fetch('https://api.example.com/...');
  // 戻り値はシリアル化され"ません"
  // Date、Map、Setなどを使用できます。
  const data = await res.json();

  return '...';
}

これは極めて重要で、"データの取得は開発者次第"という原則が実現されています。これは、開発者がデータを取得し、任意のコンポーネントを組み立てることができることを示しています。さらに、自社のコンポーネントだけでなく、Server Componentsと統合されてサーバー上で完全に動作するように設計されたTwitter埋め込みreact-tweetのような、Server Componentsのエコシステム内の任意のコンポーネントも対象となります。

app.page.js

import { Tweet } from 'react-tweet';

export default async function Page() {
  return <Tweet id="790942692909916160" />;
}

ルーターがReact Suspenseと連携しているため、コンテンツの一部がロード中でもフォールバックコンテンツをよりスムーズに表示し、必要に応じて段階的にコンテンツを表示できます。

app/page.js

import { Suspense } from 'react';
import { PostFeed, Weather } from './components';

export default function Page() {
  return (
    <section>
      <Suspense fallback={<p>フィードをロード中...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>天気をロード中...</p>}>
        <Weather />
      </Suspense>
    </section>
  );
}

さらに、ルーターはページナビゲーションをトランジションとしてマークし、ルートのトランジションが中断可能になります。

自動的なサーバーレンダリングとコード分割

Next.jsを作成した当初は、開発者がwebpack、Babel、およびその他のツールを手動で設定し、Reactアプリケーションを稼働させることが一般的でした。サーバーレンダリングやコード分割などのさらなる最適化を追加する方式は、カスタムソリューションではほとんど取り入れられていませんでした。Next.jsでは他のReactフレームワークと同様に、これらのベストプラクティスを実装し、適用するための抽象化レイヤーを作成しました。

ルートベースのコード分割とは、pages/ディレクトリ内の各ファイルがそれぞれ独立したJavaScriptバンドルに分割されるということです。これによりファイルシステムが軽量化され、初回ページロードのパフォーマンスが向上しました。

このコード分割は、Next.jsのサーバーレンダリングアプリケーションとシングルページアプリケーションの両方にとって有益でした。シングルページアプリケーションにとっても有益であった理由は、アプリケーション起動時に単一の大きなJavaScriptバンドルを読み込むことが多いからです。ただし、コンポーネントレベルのコード分割を実装するためには、開発者はnext/dynamicを使用してコンポーネントを動的にインポートする必要がありました。

app/page.tsx

import dynamic from 'next/dynamic';

const DynamicHeader = dynamic(() => import('../components/header'), {
  loading: () => <p>ロード中...</p>,
});

export default function Home() {
  return <DynamicHeader />;
}

App Routerを使用すると、Server ComponentsはブラウザのJavaScriptバンドルには含まれません。また、Client Componentsはデフォルトで自動的にコード分割されます(Next.jsのwebpackまたはTurbopackを使用)。さらに、ルーターアーキテクチャ全体がストリーミングとSuspenseを有効にしているため、サーバーからクライアントへのUIの一部を逐次送信することができます。

たとえば、条件分岐を利用することでコードパス全体を分割できます。この例では、ログアウトしたユーザーに対して、ダッシュボードのクライアント側JavaScriptをロードする必要がなくなります。

app/layout.tsx

import { getUser } from './auth';
import { Dashboard, Landing } from './components';

export default async function Layout() {
  const isLoggedIn = await getUser();
  return isLoggedIn ? <Dashboard /> : <Landing />;
}

Turbopack(ベータ版)

Turbopackは、Next.jsでテストと安定化を進めている新しいバンドルです。Turbopackを使用すると、Next.jsアプリケーションの開発速度(next dev --turboを使用)や、本番ビルドの速度(next build --turboを使用)が向上します。

Next.js 13でアルファ版がリリースされて以来、バグを修正し、不足している機能のサポートを追加してきたため、結果としてTurbopackの採用率は着実に上昇しています。私たちは、Vercel.comや多数のVercelの顧客が運用する大規模なNext.jsウェブサイトでTurbopackをドッグフーディングし、フィードバックを集め、安定性を向上させてきました。私たちのチームへのバグ報告に協力していただいたコミュニティの皆様には感謝しています。

それから6か月が経ち、現時点で私たちはベータフェーズに進む準備が整いました。

Turbopackは、まだwebpackやNext.jsと完全に同等の機能を備えているわけではありません。私たちはこのissueでその機能のサポートを追跡しています。しかし、ほとんどのユースケースはサポートされているはずです。このベータ版での私たちの目標は、採用率の増加に伴って発生する可能性のある残りのバグに対応し、将来のバージョンの安定性を確保することです。

Turbopackのインクリメンタルエンジンとキャッシングレイヤーを改善するための投資により、ローカルの開発速度だけでなく、近い将来に本番ビルドの速度も向上する予定です。将来のNext.jsのバージョンでは、next build --turbo を実行して即座にビルドできるようになりますので、ご期待ください。本日よりNext.js 13.4でnext dev --turboを使用しTurbopackのベータ版を試すことができます。

Server Actions(アルファ版)

Reactのエコシステムでは、フォーム、フォームの状態の管理、データのキャッシュと再検証に関するアイデアについて、多くの革新と探求が行われてきました。時間の経過とともに、Reactはこれらのパターンのいくつかについてより多くの意見を持つようになりました。例えば、フォームの状態を管理するために「非制御コンポーネント」を推奨しています。

現在のエコシステムのソリューションは、再利用可能なクライアントサイドのソリューションか、フレームワークに組み込まれたプリミティブのどちらかでした。しかし、これまでサーバーの変更とデータプリミティブを組み合わせる方法はありませんでした。Reactチームは、変更に対するファーストパーティーのソリューションを開発してきました。

私たちは、実験的なNext.jsのServer Actionsのサポートを発表できることをうれしく思います。 Server Actionsにより、APIレイヤーを介さずにサーバー上のデータを変更し、関数を直接呼び出すことが可能になります。

app/post/[id]/page.tsx
// app/post/[id]/page.tsx (Server Component)

import kv from './kv';

export default function Page({ params }) {
  async function increment() {
    'use server';
    await kv.incr(`post:id:${params.id}`);
  }

  return (
    <form action={increment}>
      <button type="submit">Like</button>
    </form>
  );
}

Server Actionsを使用すると、強力なサーバーファーストのデータ変更、クライアントサイドのJavaScriptのコード削減、そして段階的に強化されたフォームが利用できます。

app/dashboard/posts/page.tsx
// app/dashboard/posts/page.tsx (Server Component)

import db from './db';
import { redirect } from 'next/navigation';

async function create(formData: FormData) {
  'use server';
  const post = await db.post.insert({
    title: formData.get('title'),
    content: formData.get('content'),
  });
  redirect(`/blog/${post.slug}`);
}

export default function Page() {
  return (
    <form action={create}>
      <input type="text" name="title" />
      <textarea name="content" />
      <button type="submit">Submit</button>
    </form>
  );
}

Next.jsのServer Actionsはデータライフサイクルの他の部分、例えばNext.jsのキャッシュ、インクリメンタル静的再生成(ISR)、クライアントルーターと深く統合するように設計されています。

新しいAPIであるrevalidatePathrevalidateTagを通じてデータを再検証することで、変更、ページの再レンダリング、リダイレクトが1回のネットワークラウンドトリップで行われ、アップストリームプロバイダーが遅い場合でもクライアント上に正しいデータが表示されることが保証されます。

app/dashboard/posts/page.tsx
// app/dashboard/posts/page.tsx (Server Component)

import db from './db';
import { revalidateTag } from 'next/cache';

async function update(formData: FormData) {
  'use server';
  await db.post.update({
    title: formData.get('title'),
  });
  revalidateTag('posts');
}

export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['posts'] } });
  const data = await res.json();
  // ...
}

Server Actionsは組み合わせ可能に設計されており、Reactコミュニティに所属する誰もがServer Actionsを構築し、公開し、エコシステム内で配布することができます。Server Componentsと同様に、クライアントとサーバーの両方で利用可能な組み合わせ可能な基本要素(プリミティブ)の新時代に私たちはわくわくしています。

さらに、Server Actionsは、Next.js 13.4で今日からアルファ版で利用可能です。Server Actionsを使用することを選択すると、Next.jsはReactの実験的リリースチャンネルを使用します。

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

module.exports = nextConfig;

その他の改善点

  • ドラフトモード:ヘッドレスCMSからドラフトコンテンツを取得してレンダリングします。ドラフトモードはpagesappの両方で動作します。既存のPreview Mode APIは強化され、簡素化されましたが、pagesに対しては依然として機能します。ただし、appに対してはPreview Modeは機能しないため、ドラフトモードを使用してください。

よくある質問

App Routerの安定性とは何を意味するのでしょうか?

App Routerを今日安定版としてリリースするとは、私たちの開発作業が完了したわけではないということです。ここでの「安定性」は、App Routerの核となる部分が本番環境に対応しており、私たち自身の内部テストと多くのNext.jsの早期採用者による検証を経ていることを示しています。

私たちは、今後も更なる最適化を行っていきたいと考えています。これには、Server Actionsが完全に安定することも含まれています。今日からどこから学び始め、どのようにアプリケーションを構築すべきかをコミュニティに明確に示すために、私たちはApp Routerのコア部分の安定性に向けて努力を続けています。

App RouterはReactのcanaryチャンネル上で構築されており、Server Componentsなどの機能を導入するためのフレームワークがすでに利用可能です。詳細はこちら

App Routerの安定化はNext.js betaドキュメントにどう影響するのでしょうか?

今日から新しいアプリケーションを構築する際には、App Routerの使用を推奨します。App Routerの説明とともに一から書き直されたNext.js betaドキュメンテーションは、現在安定したNext.jsドキュメンテーションに置き換えられました。これにより、App RouterとPages Router間で簡単に切り替えることが可能になります。

App Routerの段階的な導入については、導入ガイドをご覧いただくことをお勧めします。これにより、App Routerの導入方法を学ぶことができます。

Pages Routerは廃止されるのでしょうか?

いいえ、そうではありません。私たちは引き続きpages/の開発をサポートすることを約束しています。これにはバグ修正、改善、セキュリティパッチなどが含まれ、今後も複数のメジャーバージョンで続けられます。開発者が準備が整った時点で、App Routerを段階的に導入できるように

するためです。

pages/app/を本番環境で並行して使用することは、サポートされており、推奨されています。App Routerはルートごとに導入することができます。

Server Componentsが「完成」したということは何を意味するのでしょうか?

Next.jsは、Server Componentsを含むReactアーキテクチャを採用するフレームワークの一つです。App Routerによって提供される体験が、他のフレームワークや新しいフレームワークでこのアーキテクチャを使用することを考えるきっかけとなることを期待しています。

ただし、無限スクロールのようなパターンはまだこのエコシステムで定義されていません。現状では、エコシステムが成長し、ライブラリが作成や更新される間、これらのパターンについてはクライアントサイドのソリューションの使用を推奨しています。

コミュニティ

Next.jsは、2,600人以上の個々の開発者、GoogleやMetaのような業界パートナー、そして私たちVercelのコアチームの共同作業の結果です。GitHubディスカッションRedditDiscordでコミュニティに参加してください。

このリリースは以下の皆様により提供されました:

Next.jsチーム: Andrew, Balazs, Jan, Jiachi, Jimmy, JJ, Josh, Sebastian, Shu, Steven, Tim, and Wyatt.
Turbopackチーム: Alex, Donny, Justin, Leah, Maia, OJ, Tobias, and Will.
そして貢献者たち: @shuding, @huozhi, @wyattfry, @styfle, @sreetamdas, @afonsojramos, @timneutkens, @alexkirsz, @chriswdmr, @jankaifer, @pn-code, @kdy1, @sokra, @kwonoj, @martin-wahlberg, @Kikobeats, @JTaylor0196, @sebmarkbage, @ijjk, @gnoff, @jridgewell, @sagarpreet-xflowpay, @balazsorban44, @cprussin, @ForsakenHarmony, @li-jia-nan, @dciug, @albertothedev, @DuCanhGH, @feedthejim, @patrick91, @padmaia, @sophiebits, @eps1lon, @reconbot, @acdlite, @cjmling, @nabsul, @motopods, @hanneslund, @tunamagur0, @devknoll, @apeltop, @maranomynet, @y-tsubuku, @EndangeredMassa, @ykzts, @AviAvinav, @adilansari, @wyattjoh, @charkour, @delbaoliveira, @agadzik, @Just-Moh-it, @rodrigofeijao, @leerob, @juliusmarminge, @koba04, @Phiction, @jessewarren-aa, @ryo-manba, @Yovach, and @dylanjha.

GitHubで編集を提案

Discussion