SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Next.jsがApp Routerの"次"に目指すもの

Next.js 14までの進化を振り返る──App Routerを強化する新機能を解説!

Next.jsがApp Routerの"次"に目指すもの 第1回

  • X ポスト
  • このエントリーをはてなブックマークに追加

Partial Prerendering(2)

動的コンテンツの埋め込みによりDynamic Renderingになった挙動

 それでは次に、このページをDynamic Renderingの対象にしてみましょう。通常、Dynamic Renderingの対象になる条件はcookies()headers()等の Dynamic APIを利用した場合や、fetch()関数で明示的にキャッシュ機能をオフにした場合です。

 試しに、外部APIを呼び出す動的なコンテンツを埋め込んでみましょう。リスト6は、外部APIからデータを取得して表示するコンポーネントを追加した例です。

[リスト6]外部APIからデータを取得して表示するコンポーネントを追加
// app/page.js
async function Uuid() {
  const res = await fetch("https://httpbin.org/uuid", {
    cache: "no-store", // (1)
  });

  const data = await res.json();

  return (
    <div className="p-1 border-2">
      <h2>生成完了</h2>
      <p>UUID: {data.uuid}</p>
    </div>
  )
}

 動作確認にはhttpbin.orgを使わせてもらいました。(1)で明示的にキャッシュをオプトアウトしているため、このコンポーネントを埋め込むことで、ページ全体がDynamic Renderingの対象になります。リスト7は、このコンポーネントをページに埋め込んだ例です。

[リスト7]Dynamic Renderingが行われるページの例
// app/page.js
import { Suspense } from "react";

export default async function Home() {
  return (
    <div className="flex flex-col h-screen">
      <Header />

      <div className="flex flex-1">
        <SideMenu />
        {/* メインコンテンツ */}
        <main className="flex-1 p-6">
          <h2 className="text-xl font-bold mb-4">メイン</h2>
          {/* (1) */}
          <Suspense fallback={<FallBack />}>
            <Uuid />
          </Suspense>
        </main>
      </div>
    </div>
  );
}

{/* (2) */}
function FallBack() {
  return (
    <div className="p-1 border-2">
      <p>UUIDの生成を待っています</p>
    </div>
  )
}

// 省略

 (1)でUuidコンポーネントを埋め込んでみました。画面の初期表示には間に合わなくてもいい優先度の低いコンテンツであると仮定して、 Suspenseコンポーネントを使ってローディング中の表示を行うようにしてみました。(2)でレスポンス待ちの間の表示を行うコンポーネントを定義しています。このページを動かしてみると、図5のような待ち画面が表示された後、図6のようにUUIDが表示されます。

図5:UUIDの生成を待っている画面
図5:UUIDの生成を待っている画面
図6:UUIDが表示された画面
図6:UUIDが表示された画面

 さて、ビルド結果はどうなったでしょうか。npm run buildを行うと、リスト8のような結果が得られます。

[リスト8]Dynamic Renderingが行われるページをビルドする
$ npm run build
# 省略
Route (app)                              Size     First Load JS
┌ ƒ /                                    139 B           100 kB
├ ○ /_not-found                          896 B           101 kB
└ ○ /server-actions                      139 B           100 kB
+ First Load JS shared by all            99.9 kB
  ├ chunks/4bd1b696-80bcaf75e1b4285e.js  52.5 kB
  ├ chunks/517-d083b552e04dead1.js       45.5 kB
  └ other shared chunks (total)          1.88 kB


○  (Static)   prerendered as static content
ƒ  (Dynamic)  server-rendered on demand

 リスト5から変わって、ルートディレクトリにfのマークがついています。これは、Dynamic Renderingが行われるページであることを示しています。.next/server/appフォルダを見に行くと、index.htmlやindex.rscは生成されていません(図7)。

図7:indexが事前レンダリングされなくなった
図7:indexが事前レンダリングされなくなった

 常にページ全体を動的に生成することになるので、いらないコストがかかってしまいそうです。

Partial Prerenderingで挙動がどう変わるか

 ここで、Partial Prerenderingの出番です。Uuidコンポーネントは事前に生成しておくわけにはいきませんが、Suspenseコンポーネントのfallback属性に指定したコンポーネントなら、事前に静的に生成しておいても問題ありませんよね。

 Partial Prerenderingは、Dynamic Renderingが必要な部分が含まれていたとしても、Suspenseで囲んであればページ全体としては静的に生成できるようにするための機能です。Partial Prerenderingを有効にするには、 next.config.mjsexperimental.pprプロパティを追加します。

 Next.js 15時点では、この設定はCanary版のバージョンでしか動かないことに注意してください。リスト9は、Partial Prerenderingを有効にする設定例です。

[リスト9]next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    ppr: 'incremental', // (1)
  },
};

export default nextConfig;

 (1)の設定で、Partial Prerenderingを有効にしています。incrementalは、事前レンダリングをオプトインで有効にしたページだけを処理対象にすることを意味しています。app/page.jsでオプトインの設定をしましょう。

[リスト10]Partial Prerenderingが行われるページの例
// app/page.js
// 省略
export const experimental_ppr = true
// 省略

 これで準備ができました。ビルドしてみると、リスト11のような結果が得られます。

[リスト11]Partial Prerenderingが行われるページをビルドする
$ npm run build
# 省略
Route (app)                              Size     First Load JS
┌ ◐ /                                    137 B           102 kB
├ ○ /_not-found                          894 B           102 kB
└ ○ /server-actions                      137 B           102 kB
+ First Load JS shared by all            101 kB
  ├ chunks/308-96f8fe85dc0aea53.js       46.6 kB
  ├ chunks/f5e865f6-2c6e9f68666fedff.js  53 kB
  └ other shared chunks (total)          1.88 kB


○ (Static) prerendered as static content
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed content

 ビルド結果を見ると、ルートディレクトリにPartial Prerenderingのマークがついています。.next/server/appフォルダを見に行くと、index.htmlindex.prefetch.rscが生成されていることが確認できます(図8)。

図8:indexがPartial Prerenderingされた
図8:indexがPartial Prerenderingされた

 これで、部分的に動的コンテンツを含むページでも、全体としては静的レンダリングの対象にすることができました。これにより、ページの表示速度を向上させることができそうです。これは正式実装が待ち遠しいですね。

まとめ

 Next.js 14の目玉機能である、Server ActionsとPartical Prerenderingについて解説しました。Partial PrerenderingはSuspenseと静的レンダリングのあるべき姿という感じで、App Routerの進化が感じられましたね。

 次回は、Next.js 15で追加された新機能について解説します。お楽しみに!

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/20629 2024/12/20 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング