normcore.dev

Learn Next.js 一巡して App Router について理解する

当時のNext.jsのバージョンは14.1でした。
しばらく経っているので情報が古くなっている可能性があります。

第1章 はじめに

https://nextjs.org/learn/dashboard-app/getting-started

でのメモ

新規プロジェクトの作成

npx create-next-app@latest nextjs-dashboard --use-npm --example "https://github.com/vercel/next-learn/tree/main/dashboard/starter-example"

このコースのスターターサンプルも入っているらしい。

ディレクトリ構造

  • /app:アプリケーションのすべてのルート、コンポーネント、ロジックを含みます。
  • /app/lib:再利用可能なユーティリティ関数やデータ取得関数など、アプリケーションで使用される関数が含まれています。
  • /app/ui:カード、テーブル、フォームのような、アプリケーションのすべてのUIコンポーネントを含みます。時間を節約するために、これらのコンポーネントはあらかじめスタイル設定されています。
  • /public:画像など、アプリケーションのすべての静的アセットが含まれます。
  • /script:このスクリプトは、後の章でデータベースにデータを入力するために使用します。
  • 設定ファイル:アプリケーションのルートには、next.config.jsのような設定ファイルもあります。これらのファイルのほとんどは、create-next-appを使用して新しいプロジェクトを開始するときに作成され、事前に設定されます。このコースで修正する必要はありません。

メモ

  • libの中にはモックみたいなデータがある。
  • 手作業でデータ型を宣言してるけど、型の安全性を高めるのならデータベース・スキーマに基づいて型を自動生成するPrisma をお勧めする。とのこと

2章 CSSスタイリング

Next.jsアプリケーションをスタイル設定するさまざまな方法を見てみましょう。

この章で扱うこと

  • アプリケーションにグローバルCSSファイルを追加する方法。
  • 2種類のスタイリング方法:TailwindとCSSモジュール。
  • clsxユーティリティ・パッケージを使って条件付きでクラス名を追加する方法。

グローバルスタイル

app/uiフォルダの中を見ると、global.cssというファイルがあります。

このファイルを使って、アプリケーションのすべてのルートにCSSのルールを追加できます - CSSのリセットルール、リンクのようなHTML要素のサイト全体のスタイルなどです。

アプリケーションのどのコンポーネントでもglobal.cssをインポートできますが、通常はトップレベルのコンポーネントに追加するのがよい習慣です。Next.jsでは、これがルートレイアウトです(詳細は後述します)。

/app/layout.tsxに移動し、global.cssファイルをインポートすることで、アプリケーションにグローバルスタイルを追加します:

  • → 普通にimportすれば良い
  • importするとページがいい感じにスタイルが当たる
  • tailwindも入っている
    • @tailwind ディレクティブが追加されてる
/* global */
@tailwind base;
@tailwind components;
@tailwind utilities;

Tailwindについて

  • Tailwind: ユーティリティクラスを TSX マークアップに直接すばやく記述できるようにすることで、開発プロセスをスピードアップする CSS フレームワークです。
    • Tailwindでは、クラス名を追加することで要素をスタイル化します。例えば、"text-blue-500 "というクラスを追加すると、<h1>のテキストが青くなります:
<h1 className="text-blue-500">私は青い!</h1>

CSSスタイルはグローバルに共有されますが、各クラスは各要素に個別に適用されます。
つまり、要素を追加または削除しても、別々のスタイルシートを維持したり、スタイルが衝突したり、アプリケーションの規模に応じてCSSバンドルのサイズが大きくなったりする心配はありません。

CSSモジュール

CSSモジュールでは、一意のクラス名を自動的に作成することで、CSSをコンポーネントにスコープすることができます。

メリット: CSSクラスをデフォルトでコンポーネントにローカルにスコープさせる方法を提供し、モジュール性を向上させ、スタイルの衝突のリスクを低減する。

clsxライブラリを使ってクラス名を切り替える

状態やその他の条件に基づいて、要素に条件付きでスタイルを設定する必要がある場合があります。

[clsx](https://www.npmjs.com/package/clsx)は、クラス名を簡単に切り替えることができるライブラリです。詳しくはドキュメントをご覧になることをお勧めしますが、基本的な使い方は以下の通りです:

  • ステータスを受け付けるInvoiceStatusコンポーネントを作成したいとします。ステータスは'pending'または'paid' です。
  • 支払い済み」の場合は緑色にします。未決済」の場合は灰色になります。

clsxを使って、次のように条件付きでクラスを適用することができる:

/app/ui/invoices/status.tsx

import clsx from 'clsx';

export default function InvoiceStatus({ status }: { status: string }) {
  return (
    <span
      className={clsx(
        'inline-flex items-center rounded-full px-2 py-1 text-sm',
        {
          'bg-gray-100 text-gray-500': status === 'pending', // ここ
          'bg-green-500 text-white': status === 'paid', // ここ
        },
      )}
    >
    // ...
)}

第3章 フォントと画像の最適化

  • この章のトピック
    • next/fontカスタムフォントを追加する方法。
    • next/image画像を追加する方法。
    • Next.jsでフォントと画像がどのように最適化されるか。

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

フォントはデザインにおいて重要だけど、カスタムフォントを使用すると、ファイルの取得と読み込みが必要になり、パフォーマンスに影響を与える可能性がある。

Cumulative Layout Shift(CLS 直訳: 累積レイアウトシフト) は、Googleがウェブサイトのパフォーマンスとユーザー体験を評価するために使用する指標。
フォントの場合、レイアウトシフトは、ブラウザが最初にフォールバックフォントまたはシステムフォントでテキストをレンダリングし、それが読み込まれるとカスタムフォントに置き換わるときに発生する。
この入れ替えによって、テキストのサイズ、間隔、レイアウトが変更され、周囲の要素が移動することがある。

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

プライマリフォントの追加

next/font/googleモジュールからInterフォントをインポート。

どのサブセットを読み込むかを指定します。この場合は「latin」

// /app/ui/fonts.ts
import { Inter } from 'next/font/google'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](https://tailwindcss.com/docs/font-smoothing)クラスも追加している。

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

Next.jsは、トップレベルの[/public](https://nextjs.org/docs/app/building-your-application/optimizing/static-assets)フォルダの下に画像などの静的アセットを提供する。
publicフォルダ内のファイルは、アプリケーション内で参照できる。

最適化を手動で実装する代わりに、next/imageコンポーネントを使用することで、画像を自動的に最適化することができる。

**[<Image>コンポーネント](https://nextjs.org/learn/dashboard-app/optimizing-fonts-images#the-image-component)

<Image>コンポーネントは、HTMLの<img>タグを拡張したもので、以下のような自動画像最適化機能を備えている:

  • 画像の読み込み時に自動的にレイアウトがずれないようにする。
  • ビューポートが小さいデバイスに大きな画像を送信しないように、画像のサイズを変更する。
  • デフォルトで画像を遅延ロード(ビューポートに入ると画像がロードされる)。
  • ブラウザがサポートしている場合、WebPAVIF のような最新のフォーマットで画像を提供します。
import Image from 'next/image'<Image
  src="/hero-desktop.png"
  width={1000}
  height={760}
  className="hidden md:block"
  alt="デスクトップ版を示すダッシュボードプロジェクトのスクリーンショット"
/>

この辺読んどくといいやつ


第4章 レイアウトとページの作成

https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages の作業メモを残す。

  • この章のトピック
    • ファイルシステムルーティングを使用してダッシュボードルートを作成する。
    • 新しいルートセグメントを作成する際のフォルダとファイルの役割を理解する
    • 複数のダッシュボードページ間で共有できるネストされたレイアウトを作成します。
    • コロケーション、パーシャルレンダリング、ルートレイアウトとは何かを理解する。

ネストされたルーティング

Next.jsでは、ネストされたルートを作成するためにフォルダが使用されるファイルシステムルーティングが使用される。
各フォルダはURLセグメントにマッピングされたルートセグメントを表す。

layout.tsxファイルとpage.tsxファイルを使用して、ルートごとに別々のUIを作成できる。

page.tsxは、Reactコンポーネントをエクスポートする特別なNext.jsファイルで、ルートにアクセスするために必要です。

レイアウトの作成

Next.jsでは、特別なlayout.tsxファイルを使用して、複数のページで共有されるUIを作成する。

Next.jsでレイアウトを使用する利点の1つは、ナビゲーション時にページ コンポーネントだけが更新され、レイアウトは再レンダリングされないこと。(部分レンダリング
RootLayoutは、アプリケーションすべてのページで共有される


第5章 ページ間の移動

https://nextjs.org/learn/dashboard-app/navigating-between-pages の作業メモ

  • この章のトピック
    • next/linkコンポーネントの使い方
    • usePathname()フックでアクティブなリンクを表示する方法
    • Next.jsのナビゲーションの仕組み

<a>タグで遷移すると、画面すべてが更新されてしまうので、ナビゲーションを最適化したほうがよい。

<Link>コンポーネント

Next.jsでは、<Link />コンポーネントを使用して、アプリケーション内のページ間をリンクすることができる
<Link>を使用すると、JavaScriptでクライアントサイドナビゲーションを行うことができる。

自動コード分割とプリフェッチ

ナビゲーション体験を向上させるために、Next.jsはルートセグメントごとにアプリケーションを自動的にコード分割する。
これは、ブラウザが初期ロード時にすべてのアプリケーションコードを読み込む、従来のReactSPA とは異なる。

ルートでコードを分割するということは、ページが分離されることを意味する。あるページがエラーを起こしても、アプリケーションの残りの部分は動作する。

さらに、本番環境では、<Link>コンポーネントがブラウザのビューポートに表示されるたびに、Next.jsはリンク先のルートのコードをバックグラウンドで自動的にプリフェッチする

(本番環境でのみで開発環境ではプリフェッチしないようです)
ユーザーがリンクをクリックするころには、リンク先ページのコードはすでにバックグラウンドで読み込まれている。

ナビゲーションの仕組みについて詳くはこちら: https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#how-routing-and-navigation-works

パターン: アクティブリンクの表示

一般的なUIパターンは、ユーザーが現在どのページにいるのかを示すために、アクティブなリンクを表示する。

これを行うには、URLからユーザーの現在のパスを取得する必要がある。
Next.jsはusePathname()というフックを提供しており、これを使用してパスをチェックし、このパターンを実装することができる。
"use client"ディレクティブをファイルの先頭に追加する必要がある。


[Learn Next.js] 第 6 章 データベースの設定

https://nextjs.org/learn/dashboard-app/setting-up-your-database の作業ログ

@vercel/postgres  を使って PostgreSQL データベースをセットアップする。

  • この章のトピック

    • プロジェクトを GitHub にプッシュする。
    • Vercel アカウントをセットアップし、GitHub レポをリンクすると、即座にプレビューとデプロイができます。
    • プロジェクトを作成し、Postgres データベースにリンクします。
    • データベースに初期データをシードする。
  • Versel 使うので、リポジトリと Vsersel のサービスを連携する。

  • Versel 上の Postgress データベースを作成

  • ローカルの設定 .env

  • npm i @vercel/postgres

  • package.json に seed 用の script 追加

seed 実行

$ npm run db:seed

> db:seed
> node -r dotenv/config ./scripts/seed.js

Created "users" table
Seeded 1 users
Created "customers" table
Seeded 10 customers
Created "invoices" table
Seeded 15 invoices
Created "revenue" table
Seeded 12 revenue
  • データは Vercel の管理画面からも確認できる。SQL も叩ける。

第 7 章- データの取得(Data Fetching)

https://nextjs.org/learn/dashboard-app/fetching-data での作業ログ

  • この章のトピック
    • データをフェッチするためのいくつかのアプローチについて学ぶ:API、ORM、SQL など。
    • Server Components を使用することで、バックエンドのリソースにより安全にアクセスできるようになる。
    • ネットワーク ウォーターフォールとは何か。
    • JavaScript パターンを使って並列データフェッチを実装する方法。

データ取得方法の選択

API レイヤー

API は、アプリケーション・コードとデータベースの間の仲介レイヤーです。API を使うケースはいくつかある:

  • API を提供するサードパーティのサービスを使用している場合。
  • クライアントからデータを取得する場合、データベースの秘密がクライアントに漏れるのを避けるために、サーバー上で動作する API レイヤーを用意したい。
    Next.js では、ルートハンドラを使ってAPI エンドポイントを作成できる

データベースクエリ

フルスタックのアプリケーションを作成する場合、データベースとやりとりするためのロジックを書く必要もあります。Postgres のようなリレーショナルデータベースの場合、SQL やPrisma  のようなORMを使ってこれを行うことができる。
データベースのクエリーを書かなければならない場合がいくつかある:

  • API エンドポイントを作成する際には、データベースとやり取りするためのロジックを記述する必要がある。
  • React Server Components(サーバー上でデータを取得する)を使用している場合は、API レイヤーをスキップして、データベースの秘密をクライアントに公開するリスクを冒すことなく、データベースに直接問い合わせることができる。

サーバーコンポーネントを使用してデータを取得する

デフォルトでは、Next.js アプリケーションはReact Server Componentsを使用する。
Server Components を使ったデータ取得は比較的新しいアプローチで、これを使うメリットがいくつかある:

  • Server Components は promise をサポートし、データフェッチなどの非同期タスクをよりシンプルに解決する。
  • useEffectや useState、データ取得ライブラリを使わず、async/await構文を使用できる。
  • サーバーコンポーネントはサーバー上で実行されるため、重いデータ取得やロジックをサーバー上に保持し、その結果のみをクライアントに送信することができる。
  • 前述したように、サーバーコンポーネントはサーバー上で実行されるため、API レイヤーを追加することなく、データベースに直接問い合わせることができる。

SQL の使用

ダッシュボード・プロジェクト(いまやってるプロジェクト)では、Vercel Postgres SDKと SQL を使用してデータベース・クエリを記述します。SQL を使う理由はいくつかある:

  • SQL は、リレーショナルデータベースをクエリするための業界標準である(例えば、ORM は SQL を生成する)。
  • SQL の基本を理解することで、リレーショナル・データベースの基本を理解することができ、その知識を他のツールに応用することができる。
  • SQL は汎用性が高く、特定のデータを取得したり操作したりすることができる。
  • Vercel Postgres SDK はSQL インジェクションからの保護を提供する。

リクエストウォーターフォールとは?

ウォーターフォールとは前のリクエストの完了に依存する一連のネットワーク・リクエストのことである。データ・フェッチの場合、各リクエストは、前のリクエストがデータを返して初めて開始できる。

const revenue = await fetchRevenue();
const latestInvoices = await fetchLatestInvoices(); //  fetchRevenue() の終了を待つ。
const {
  numberOfInvoices,
  numberOfCustomers,
  totalPaidInvoices,
  totalPendingInvoices,
} = await fetchCardData(); // fetchLatestInvoices() の終了を待つ。

並列データフェッチ

ウォーターフォールを避ける一般的な方法はすべてのデータ要求を同時に、つまり並行して開始することである。
JavaScript では、Promise.all()、またはPromise.allSettled()関数を使うと、すべてのプロミスを同時に開始することができる。

export async function fetchCardData() {
try {
    const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
    const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
    const invoiceStatusPromise = sql`SELECT
        SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
        SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
        FROM invoices`;

   // ここ
    const data = await Promise.all([
    invoiceCountPromise,
    customerCountPromise,
    invoiceStatusPromise,
    ]);
    // ...
}
}

このパターンを使うことで、次のことができる:

  • すべてのデータ・フェッチを同時に実行し始めることで、パフォーマンスを向上させることができる。
  • どんなライブラリやフレームワークにも適用できるネイティブの JavaScript パターンを使う。
    この JavaScript のパターンだけに頼ることには 1 つ不利な点がある。1 つのデータリクエストが他のすべてのリクエストより遅い場合はどうなるのか?

第 8 章 Static Renderting と Dynamic Rendering

この章のトピック

  • 静的レンダリングとは何か、そしてそれがどのようにアプリケーションのパフォーマンスを向上させるのか。
  • ダイナミック・レンダリングとは何か、どのような場合に使うのか。
  • ダッシュボードをダイナミックにするさまざまなアプローチ。
  • 低速のデータ・フェッチをシミュレートして、何が起こるかを確認する。

Static Rendering とは

  • データの取得とレンダリングはビルド時(デプロイ時)または Revalidate 時にサーバー上で行われる。
  • その結果をCDNに配信しキャッシュすることができる。
  • ユーザーがアプリケーションにアクセスするたびにキャッシュされた結果が提供される
  • 静的レンダリングの利点
  • 高速なウェブサイト
    • プリレンダリングされたコンテンツはキャッシュされ、グローバルに配信されます。これにより、世界中のユーザーがより迅速かつ確実にお客様のウェブサイトのコンテンツにアクセスできるようになる
  • サーバー負荷の軽減
    • コンテンツがキャッシュされるため、サーバーはユーザーのリクエストごとにコンテンツを動的に生成する必要がない
  • SEO
    • プリレンダリングされたコンテンツは、ページが読み込まれた時点ですでに利用可能な状態になっているため、検索エンジンのクローラーがインデックスしやすくなる。
  • 向いているページ
  • 静的なブログ記事・製品ページなど、データのない UI やユーザー間で共有されるデータに便利。
  • 向いていないページ
  • 定期的に更新されるパーソナライズされたデータを持つダッシュボードには向いていない

Dynamic Rendering とは

リクエスト時(ユーザーがページにアクセスした時)に各ユーザーに対してサーバー上でコンテンツがレンダリングされる。

  • 動的レンダリングの利点
  • リアルタイムデータ
    • ダイナミックレンダリングにより、アプリケーションはリアルタイムまたは頻繁に更新されるデータを表示でる。これは、データが頻繁に変更されるアプリケーションに最適。
  • ユーザー固有のコンテンツ
    • ダッシュボードやユーザープロファイルなど、パーソナライズされたコンテンツを提供し、ユーザーとのインタラクションに基づいてデータを更新することが容易になる。
  • リクエスト時の情報
    • 動的レンダリングでは、クッキーや URL 検索パラメータなど、リクエスト時にしかわからない情報にアクセスできる。

ダッシュボードを Dynamic に

  • サーバーコンポーネントやデータ取得関数の内部でunstable_noStoreと呼ばれる Next.js API を使用すると、静的レンダリングを省略できる
// ...
import { unstable_noStore as noStore } from "next/cache";

export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  noStore();

  // ...
}

export async function fetchLatestInvoices() {
  noStore();
  // ...
}
// ...

低速データフェッチのシミュレーション

export async function fetchRevenue() {
  try {
    console.log("Fetching revenue data...");
    // ここ
    await new Promise((resolve) => setTimeout(resolve, 3000));

    const data = await sql<Revenue>`SELECT * FROM revenue`;

    console.log("Data fetch completed after 3 seconds.");

    return data.rows;
  } catch (error) {
    console.error("Database Error:", error);
    throw new Error("Failed to fetch revenue data.");
  }
}

開発者が解決しなければならない共通の課題に行き着く:
ダイナミック・レンダリングでは、アプリケーションは最も遅いデータ・フェッチと同じ速度しか出せない。


第 9 章 Streaming

第 8 章では、ダッシュボードページをダイナミックレンダリングにし、データフェッチの遅さがアプリケーションのパフォーマンスにどのような影響を与えるのかについて説明していた。
ここでは、遅いデータリクエストがあったときにユーザーエクスペリエンスを改善する方法を見ていく。
この章のトピック

  • ストリーミングとは何か?
  • loading.tsxSuspense でストリーミングを実装する方法。
  • ローディング・スケルトンとは何か。
  • ルートグループとは何か、どのような場合に使用するのか。
  • アプリケーションのどこにサスペンス境界線を配置するか。

Streaming(ストリーミング)とはなにか?

ストリーミングとは、ルートを小さな "チャンク "に分割し、準備ができ次第、サーバーからクライアントに順次ストリーミングするデータ転送技術である。

ストリーミングによって、遅いデータ・リクエストがページ全体をブロックするのを防ぐことができる。
ストリーミングは React のコンポーネント・モデルと相性がよく、各コンポーネントはチャンクとみなすことができるからだ。
Next.js でストリーミングを実装する方法は 2 つ:

  1. ページレベルでは、loading.tsxファイル
    1. Susppense の上に構築された特別な Next.js ファイルで、ページのコンテンツがロードされる間に、代替として表示するフォールバック UI を作成できる
  2. 特定のコンポーネントについては、<Suspense> を利用する
    ストリーミングの利点 → チャンクは並列にレンダリングされ、全体のロード時間が短縮される。

loading.tsx

export default function Loading() {
  return <div>Loading...</div>;
}
  • Susppense の上に構築された特別な Next.js ファイルで、ページのコンテンツがロードされる間、代替として表示するフォールバック UI を作成できる
  • 静的コンテンツはフォールバック UI の表示にならずすぐ表示される
  • デフォルトでは設置したディレクトリ以下に適用される
  • 絞りたい場合はRouteGroupsで変更できる
    • /(overview) ディレクトリのようにディレクトリ名を () で囲う

ローディングスケルトンを表示

ローディングスケルトンはロード後に表示される UI の簡略版。

Route Group

  • ルートグループを使用すると、URL パスの構造に影響を与えることなく、ファイルを論理的なグループにまとめることができる。
  • ()を使用して新しいフォルダを作成すると、その名前は URL パスに含まれません。つまり、/dashboard/(overview)/page.tsxは /dashboardになる。

コンポーネントのストリーミング(React Suspense)

React Suspense を使えばより細かく特定のコンポーネントをストリーミングすることができる。
Susppense では、何らかの条件が満たされるまで(例えばデータが読み込まれるまで)、アプリケーションの一部のレンダリングを延期することができる。
動的コンポーネントを Susppense でラップすることができる。
そして、動的コンポーネントのロード中に表示するフォールバックコンポーネントを渡す。

サスペンスの境界線をどこに置くかを決める

サスペンスの境界線をどこに置くかは、いくつかの事柄による:

  1. ユーザーにどのようにページを体験してもらいたいか。
  2. どのコンテンツを優先するか
  3. コンポーネントがデータ・フェッチに依存している場合。
    ダッシュボードのページを見て、もっと違ったやり方はなかったか?
    → 心配しないで。正解はない。
  • loading.tsxで行ったように、ページ全体をストリーミングすることもできるが......コンポーネントの 1 つに遅いデータ・フェッチがある場合、ロード時間が長くなる可能性がある。
  • すべてのコンポーネントを個別にストリーミングすることもできるが......その場合、準備が整うにつれて UI が画面に飛び込んでくる可能性がある。
  • ページセクションをストリーミングすることで、時差効果を作り出すこともできる。ただし、ラッパー・コンポーネントを作成する必要がある。
    サスペンスの境界をどこに置くかは、アプリケーションによって異なる。
    一般的には、データの取得を必要なコンポーネントに移し、そのコンポーネントをサスペンスでラップするのが良い方法です。
    しかし、アプリケーションに必要であれば、セクションやページ全体をストリーミングしても問題はありません。
    Suspense は、より楽しいユーザー体験を生み出すための強力な API である。
  • Q: 一般的に、サスペンデッドとデータ・フェッチで作業する場合、どのようなことがグッドプラクティスとされていますか?
  • A: データ・フェッチを必要なコンポーネントに移す - データ取得を必要なコンポーネントに移すことで、よりきめ細かい Suspension 境界を作ることができます。これにより、特定のコンポーネントをストリーミングし、UI がブロックされるのを防ぐことができる。
    10 章ではストリーミングを念頭に置いて Next.js の新しいレンダリングモデルである部分プリレンダリングをまあ穴部

第 10 章 Partial Prerendering (Optional)

Note: 部分プリレンダリングは、Next.js 14 で導入された実験的な機能です。
このページの内容は、この機能が安定するにつれて更新される可能性があります。
実験的な機能を使いたくない場合は、この章を読み飛ばしてもかまいません。

  • この章のトピック
  • パーシャル・プリレンダリングとは何か。
  • 部分プリレンダリングの仕組み

静的コンテンツと動的コンテンツの組み合わせ

現在、ルートの内部で動的な関数(たとえばnoStore()  やcookies()  などを呼び出すと、ルート全体が動的になる。
今日、ほとんどのウェブアプリケーションはこの方法で作られている。アプリケーション全体または特定のルートに対して、静的レンダリングと動的レンダリングのどちらかを選択する。
しかし、ほとんどのルートは完全に静的でも動的でもない。静的・動的なコンテンツの両方を持つルートがあるかもしない。

  • 例) 学習中に作っているダッシュボードアプリ
  • 静的: データに依存せず、ユーザーにパーソナライズされない
  • 動的: 頻繁に変更されるデータに依存し、ユーザーにパーソナライズされる

パーシャル・プリレンダリングとは?

部分的なプリレンダリングのプレビューが含まれています。これは実験的な機能で、静的なローディングシェルでルートをレンダリングする一方で、動的な部分を残すことができる。
つまり、ルートの動的な部分を分離することができる。

  • ユーザーがルート訪問する時
  • スタティック・ルート・シェルが提供され、初期ロードが速くなる。
  • シェルは、動的コンテンツが非同期でロードされる穴(hole)を残す。
  • 非同期のホールは並列にストリーミングされ、ページ全体のロード時間を短縮する。
    これは、ルート全体が完全に静的または動的である今日のアプリケーションの動作とは異なる。
    パーシャル・プリレンダリングは、超高速の静的エッジ配信と完全な動的機能を組み合わせたもので、静的サイト生成と動的配信の長所を併せ持つ、ウェブ・アプリケーションのデフォルトのレンダリング・モデルになる可能性があると、私たちは信じています(らしい。)
    shell(殻): Suspence を包んでいる殻のこと
    hole(穴): ダイナミックな部分
  • Q: パーシャル・プリレンダリングの穴(hole)とは?
  • A: 動的コンテンツが非同期にロードされる場所(穴とは、リクエスト時に動的コンテンツが非同期でロードされる場所のこと)

部分プリレンダリングの仕組み

Partial Prerendering は、React のConcurrent APIを活用し、Susppenseを使用して、何らかの条件が満たされるまで(例えばデータがロードされるまで)アプリケーションの一部のレンダリングを延期する。
フォールバックは他の静的コンテンツとともに最初の静的ファイルに埋め込まれる。
ビルド時 (もしくは再バリデーション時) に、ルートの静的な部分はプリレンダリングされ、残りはユーザーがルートをリクエストするまで延期されます。
Susppense でコンポーネントをラップしても、コンポーネント自体が動的になるわけではなく(この動作を実現するためにunstable_noStoreを使用したことを覚えておいてください)、むしろ Susppense はルートの静的な部分と動的な部分の境界として使用されることに注意が必要。
部分プリレンダリングのすばらしいところは、コードを変更しなくても使えること。
ルートの動的な部分をラップするためにサスペンスを使用している限り、Next.js はルートのどの部分が静的で、どの部分が動的かを知ることができる。

Note: Partial Prerendering の設定方法については、Partial Prerendering (experimental) ドキュメントを参照するか、Partial Prerendering テンプレートとデモ  をお試しください。この機能は実験的なものであり、まだ本番配備の準備ができていないことに注意することが重要です。

概要

要約すると、アプリケーションのデータ・フェッチを最適化するために、あなたはいくつかのことを行った:

  1. アプリケーションコードと同じリージョンにデータベースを作成し、サーバーとデータベース間の待ち時間を短縮。
  2. React Server Components でサーバー上のデータをフェッチします。
    1. これにより、高価なデータフェッチとロジックをサーバー上に保持し、クライアント側の JavaScript バンドルを削減し、データベースの秘密がクライアントに公開されるのを防ぐことができる。
  3. SQL を使用して必要なデータのみを取得し、リクエストごとに転送されるデータ量と、インメモリでデータを変換するために必要な JavaScript の量を削減します。
  4. JavaScript によるデータ取得の並列化 - そうすることに意味がある場合。
  5. 遅いデータリクエストがページ全体をブロックするのを防ぎ、ユーザーがすべての読み込みを待つことなく UI とのやりとりを開始できるように、ストリーミングを実装しました。
  6. データ取得を必要なコンポーネントに移すことで、Partial Prerendering に備えてルートのどの部分をダイナミックにすべきかを切り分けることができます。
    第 11 章では、データを取得する際に実装する必要のある 2 つの一般的なパターン、検索とページネーションについて見ていきます。

第 11 章 Adding Search and Pagination / 検索とページネーションの追加

  • この章のトピック
  • Next.js の API であるsearchParamsusePathnameuseRouterの使い方を学ぶ。
  • URL 検索パラメータを使用した検索とページネーションの実装。

なぜ URL 検索パラメータを使うのか?

URL パラメータを使って検索を実装することには、いくつかの利点がある:

  • ブックマークおよび共有可能な URL: 検索パラメータは URL 内にあるため、ユーザーは検索クエリやフィルタを含むアプリケーションの現在の状態をブックマークし、将来の参照や共有に利用できる。
  • サーバーサイド・レンダリングと初期ロード: URL パラメーターをサーバー上で直接使用して初期状態をレンダリングできるため、サーバー レンダリングの処理が容易になる
  • アナリティクスとトラッキング: URL パラメーターをサーバー上で直接使用して初期状態をレンダリングできるため、サーバー レンダリングの処理が容易になる。

検索機能の追加

  • Next.js のフック

  • useSearchParams- 現在の URL のパラメータにアクセスできる。

    • たとえば、この URL の検索パラメータは/dashboard/invoices?page=1&query=pending
    • の場合、次のようになる{page: '1', query: 'pending'}
  • usePathname- 現在の URL のパス名を読み取ることができる。

    • たとえば、ルートが/dashboard/invoices
    • の場合usePathname'/dashboard/invoices'を返す。
  • useRouter- クライアント コンポーネント内のルート間のナビゲーションを有効にする

  • 実装手順の概要

  1. ユーザーの入力をキャプチャする。
  2. 検索パラメータで URL を更新する。
  3. URL を入力フィールドと同期させておく。
  4. 検索クエリを反映させるためにテーブルを更新する。

defaultValue vs. value / Controlled vs. Uncontrolled

  • 制御コンポーネント: value と onChange をバインドする →React で値を管理する
  • 非制御コンポーネント: defaultValue と ref をバインドする →DOM で値を管理する
  • React 公式にも書いてあるとおり、defaultValue はコンポーネントのマウント時のみ設定され、その後、仮に値を変更してもフォーム要素の値が書き換わることはない。
    入力の値を管理するためにステートを使用する場合、value属性を使用してコントロールされたコンポーネントにする。これは、React が入力の状態を管理することを意味する。
    しかし、ステートを使用しないので、defaultValueを使用することができる。
    これは、ネイティブ入力が独自の状態を管理することを意味し、ステートの代わりに検索クエリを URL に保存しているので問題がない。

useSearchParams()フックとsearchParamsプロップの使い分けは?

検索パラメータを抽出するために 2 つの異なる方法を使用していることにお気づきかもしれません。どちらを使うかは、クライアントで作業しているかサーバで作業しているかによって異なります。

  • <Search>はクライアント・コンポーネントなので、クライアントからパラメータにアクセスするためにuseSearchParams()フックを使用しました。
  • <Table>はそれ自身のデータをフェッチする Server Component なので、searchParamsプロップをページからコンポーネントに渡すことができます。
    一般的なルールとして、クライアントからパラメータを読み込みたい場合は、useSearchParams()フックを使用します。

第 12 章 Data Mutation

請求書を作成、更新、削除する機能を追加する
12 章のトピック

  • React Server Actions とは何か、そしてそれを使ってどのようにデータを変異させるのか。
  • フォームとサーバーコンポーネントの扱い方
  • 型検証を含む、ネイティブのformDataオブジェクトを扱うためのベストプラクティス。
  • revalidatePathAPI を使用してクライアント・キャッシュを再検証する方法。
  • 特定の ID を持つダイナミック・ルート・セグメントを作成する方法。

サーバーアクションとは?

React Server Actions を使用すると、非同期コードをサーバー上で直接実行できる。
これにより、データを変更するための API エンドポイントを作成する必要がなくなる。
その代わりに、サーバー上で実行される非同期関数を記述し、クライアントまたはサーバーコンポーネントから呼び出すことができる。
Web アプリケーションは様々な脅威にさらされやすいため、セキュリティは最優先事項である。
そこでサーバーアクションの出番です。効果的なセキュリティソリューションを提供し、さまざまなタイプの攻撃から保護し、データを保護し、認証されたアクセスを保証される。
Server Actions は、POST リクエスト、暗号化されたクロージャ、厳密な入力チェック、エラーメッセージのハッシュ化、ホストの制限などの技術によってこれを実現し、これらすべてが連携してアプリの安全性を大幅に高める。

サーバーアクションでフォームを使用する

React では、<form>要素の action 属性を使ってアクションを呼び出すことができる。
アクションは自動的に、取り込まれたデータを含むネイティブの FormData オブジェクトを受け取る。

// Server Component
export default function Page() {
  // Action
  async function create(formData: FormData) {
    "use server";

    // Logic to mutate data...
  }

  // action属性を使ってアクションを呼び出す
  return <form action={create}>...</form>;
}

Next.js とサーバーアクション

サーバーアクションは、Next.js のキャッシュ  にも深く統合されている。
サーバーアクションでフォームが送信されると、アクションを使用してデータを変更できるだけでなく、revalidatePathrevalidateTag  などの API を使用して、関連するキャッシュを再検証することもできる。

  • Q: サーバー・アクションを使うメリットは何ですか?

  • A: プログレッシブ・エンハンスメント(フォーム用の JavaScript がまだ読み込まれていない場合や、読み込みに失敗した場合でも、ユーザーがフォームとやりとりしてデータを送信できるようになる。)

  • Next.js の[revalidatePath](https://nextjs.org/docs/app/api-reference/functions/revalidatePath)() : このキャッシュをクリアして、サーバーへの新しいリクエストをトリガーする。サーバーから最新のデータを取得される

  • Next.js の redirect() : ページにリダイレクトする


第 13 章 Handling Errors

この章のトピック

  • error.tsxファイルを使用して、ルートセグメントでエラーをキャッチし、ユーザーにフォールバック UI を表示する方法。
  • notFound関数とnot-foundファイルを使用して 404 エラー(存在しないリソース)を処理する方法。

すべてのエラーを error.tsx で処理

error.tsxファイルは、ルートセグメントの UI 境界を定義するために使用できる。
これは、予期しないエラーのためのキャッチオールとして機能し、ユーザーにフォールバック UI を表示することができます。

  • "use client"-error.tsxはClient Component である必要がある
  • 2 つの props を受け取る
    • error: このオブジェクトは JavaScript のネイティブErrorオブジェクトのインスタンスです。
    • reset: エラー境界をリセットする関数です。実行されると、この関数はルートセグメントの再レンダリングを試みる

notFound関数で 404 エラーを処理する

error.tsxは すべてのエラーをキャッチするのに便利だが、notFoundは存在しないリソースをフェッチしようとしたときに使用できる。

より具体的にしたい場合は、404 エラーを表示して、アクセスしようとしているリソースが見つからなかったことをユーザーに伝えることができる。

not-found.tsx を作成し、notFound() を呼ぶと 404 向け画面が表示される。

notFoundは error.tsxよりも優先されるので、より具体的なエラーに対処したい場合は、notFoundを使うことができる!


第 14 章 アクセシビリティの向上

ここでは、Server Actions を使ってサーバーサイドのバリデーションを実装する方法と、useFormStateフックを使ってフォームエラーを表示する方法を、アクセシビリティを考慮しながら見ていく

14 章のトピック

  • Next.js でeslint-plugin-jsx-a11yを使用してアクセシビリティのベストプラクティスを実装する方法
  • サーバーサイドのフォームバリデーションの実装方法
  • React のuseFormStateフックを使ってフォームエラーを処理し、ユーザーに表示する方法

アクセシビリティとは何か

アクセシビリティとは、障害のある人を含め、誰もが使用できるウェブアプリケーションを設計し、実装することを指す。
キーボードナビゲーション、セマンティック HTML、画像、色、動画など、多くの分野をカバーする広大なトピックである。
この章では Next.js で利用可能なアクセシビリティ機能と、アプリケーションをよりアクセシブルにするための一般的なプラクティスについて説明する。

アクセシビリティについてより学ぶのなら web.dev のLearn Accessibilityがおすすめ

サーバーサイドの検証

サーバー上でフォームを検証することで、次のことが可能になる:

  • データをデータベースに送信する前に、データが期待される形式であることを確認してください。
  • 悪意のあるユーザーがクライアント側のバリデーションをバイパスするリスクを低減する。
  • 何が有効なデータとみなされるのか、真実の情報源はひとつである。

第 15 章 認証

認証と認可

  • 認証とは、ユーザーが本人であることを確認することです。ユーザー名とパスワードのようなもので、本人であることを証明する。
  • 認可は次のステップである。ユーザーの身元が確認されると、認可はアプリケーションのどの部分の使用を許可するかを決定する。

つまり、認証はあなたが誰であるかをチェックし、認可はあなたがアプリケーションで何ができるか、何にアクセスできるかを決定する。

  • ここでは NextAuth を使った例を試してみる。
    • この時点では Next.js 14 に対応しているのは NextAuth.js の beta 版になるらしい

第 15 章 メタデータの追加

トピック

  • メタデータとは何か。
  • メタデータの種類。
  • メタデータを使って Open Graph 画像を追加する方法。
  • メタデータを使ってファビコンを追加する方法。

メタデータとは何か?

ウェブ開発において、メタデータはウェブページに関する追加的な詳細を提供する。
メタデータは、ページを訪れたユーザーには見えない。
その代わり、ページの HTML 内、通常は<head>要素内に埋め込まれ、舞台裏で機能します。この隠された情報は、検索エンジンや、ウェブページのコンテンツをよりよく理解する必要のある他のシステムにとって非常に重要である。

なぜメタデータが重要なのか?

メタデータはウェブページの SEO を強化する上で重要な役割を果たし、検索エンジンやソーシャルメディアプラットフォームにとってよりアクセスしやすく、理解しやすいものにします。適切なメタデータは、検索エンジンがウェブページを効果的にインデックスし、検索結果でのランキングを向上させるのに役立つ。
さらに、Open Graph のようなメタデータは、ソーシャルメディア上で共有されたリンクの見栄えを改善し、コンテンツをユーザーにとってより魅力的で有益なものにする。

メタデータの追加

Next.js には、アプリケーションのメタデータを定義するための Metadata API がある。
アプリケーションにメタデータを追加するには 2 つの方法がある

  • 設定ベース静的なメタデータオブジェクトまたは動的なgenerateMetadata関数を layout.jsまたはpage.jsファイルにエクスポートします。
  • ファイルベース:Next.js には、メタデータ用に特別なファイルが用意されています:
    • favicon.icoapple-icon.jpgicon.jpg:ファビコンとアイコンに使用。
    • opengraph-image.jpg と  twitter-image.jpg:ソーシャルメディアの画像に使用。
    • robots.txt:検索エンジンのクロールを指示する
    • sitemap.xml:ウェブサイトの構造に関する情報を提供

ページのタイトルと説明

layout.jsや page.jsファイルからメタデータ・オブジェクトをインクルードして、タイトルや説明文などのページ情報を追加することもできる。

layout.js のメタデータは、それを使用するすべてのページに継承されます。

import { Metadata } from "next";

export const metadata: Metadata = {
  title: "Acme Dashboard",
  description: "The official Next.js Course Dashboard, built with App Router.",
  metadataBase: new URL("https://next-learn-dashboard.vercel.sh"),
};

export default function RootLayout() {
  // ...
}

以上で本編完!

第 17 章

Next.js を探求し続けるためのリソースをいくつかご紹介します: