Loading...

Next.js

2024/02/23 10:26
2024/12/23 14:36

App Router

see also: Docs | Next.js
主な機能は以下のとおり
  • Routing: ファイルシステムベースで Server Components, layouts, nested routing, loading states, error handling などをサポートする
  • Rendering: Server Components にて Server-side rendering を、Client Components にて Client-side rendering をそれぞれサポートし、さらにサーバー側での静的/動的レンダリングや Edge, Node.js 上でのストリーミングにも対応している
  • Data Fetching: Server Components 上での async/await によるシンプルな data fetching、および memo, cache, revalidation 機能による fetch API の拡張をサポート
  • Styling: CSS Modules, Tailwind CSS, CSS-in-JS などスタイリングの手法を自由に選択できる
  • Optimizations: Image, Fonts, Script の最適化をサポート
  • TypeScript: TypeScript をサポート

プロジェクト構造

  • Top-level folders
    • 📁 app: App Router
      • 📝 layout.tsx: Layout
      • 📝 page.tsx: Page
      • 📝 loading.tsx: Loading UI
      • 📝 not-found.tsx: Not Found UI
      • 📝 error.tsx: Error UI
      • 📝 global-error.tsx: Global error UI
      • 📝 route.tsx: API endpoint
      • 📝 template.tsx: Re-rendered layout
      • 📝 default.tsx: Parallel router fallback page
    • 📁 pages: Pages Router(App Router と共存可能)
    • 📁 public: 静的なアセット
    • 📁 src: アプリケーションソースコードフォルダ(Optional)
  • Top-level files
    • 📝 next.config.ts: Next.js の設定ファイル
    • 📝 instrumentation.ts: OpenTelemetry and Instrumentation 用のファイル
    • 📝 middleware.ts: Next.js リクエストミドルウェア
    • 📝 next-env.d.ts: Next.js のための TypeScript 型定義ファイル
    • 📝 package.json, .env, .eslintrc.json, .gitignore, tsconfig.json, etc...

Routing

基本的なルーティング

  • Next.js では Pages Router に引き続き App Router でもファイルシステムをベースとしたルーティング定義を行う
  • 各フォルダのことを route segment よび、これは URL セグメントと対応する
    • この route segment という用語はドキュメント中に頻繁に登場する(see also: Route Segments
  • /hoge/fuga のようにネストされたルートを作成したい場合、フォルダを入れ子にする
  • 各 route segment には下記のようなファイルを作成する
    • Pages: 該当のルート固有の UI を記述するファイル
    • Layouts: 該当ルート以下のセグメントに共通した UI を記述するファイル
    • Templates: TODO
    • Metadata: Head タグ内に定義するようなメタデータを指定できるファイルだが、Layout や Page 内でも generateMetadata 関数を export することにより実現可能ではある
  • Project Organization and File Colocation
    • 各フォルダに page.tsx か route.ts を配置するまで各ルートは public にアクセスできない
    • page.tsx か route.ts を配置が配置されたあとも取得できるコンテンツはそれらから返却された内容だけとなる
    • 該当ルートで利用する他のファイルを配置しても問題はないので、例えば route segment ごとに components/lib などのディレクトを配置しても問題ない

ルーティングの各種機能

  • Route Groups
    • URL paths に影響を及ぼさずに app 配下のフォルダをグルーピングできる機能
    • 例えばコンテンツに関するルートである /blogs, /videos などを contents フォルダ配下にまとめたい場合、(contents)/blogs, (contents)/videos というかたちで app 配下に置くと URL path に影響を及ぼさずにグルーピングができる
    • これによりパスの構造に依存せずに Layout を定義することも可能となる
    • 異なる Route Groups が違いに同じ URL path に解決してしまう場合、エラーとなるので注意が必要
    • 異なる Root Layout 間のパスへ遷移するときには full reload が走ってしまう点にも注意
  • Dynamic Routes
    • パスパラメータとなる Route Segments を [param] という名前のフォルダを作成することにより実現できる
    • Page に generateStaticParams 関数 を定義することによりあらかじめ特定のパラメタ値に対するページをレンダリングしておくこともできるし、リクエスト時にレンダリングすることもできる
    • 以下のような複雑な Dynamic Route も定義可能
      • Catch-all Segments: 複数のセグメントをパラメータとしてキャッチできる機能で、[...slug] という名前でフォルダを定義し、Page 側では string[] で値を拾える
      • Optional Catch-all Segments: パラメータが存在しない場合も含め複数のセグメントをパラメータとしてキャッチできる機能で、[[...slug]] という名前でフォルダを定義し、パラメータが存在しない場合、Page 側では {} という値を拾える
  • Parallel Routes
    • TODO
  • Intercepting Routes
    • TODO

リンクとナビゲーション

TODO

Route Handler

TODO

Middleware

TODO

Internationalization

TODO

Data Fetching

Data Fetching の手法

  1. サーバーサイドでの fetch の利用
  2. サーバーサイドでのサードパティライブラリの利用
  3. クライアントサイドでの Router Handler の利用
  4. クライアントサイドでのサードパティライブラリの利用

サーバーサイドでの fetch の利用

  • Next.js は fetch Web API を拡張して cache や revalidate の振る舞いを持たせている
  • Next.js はデフォルトで fetch により得られた値をサーバー上の Data Cache に自動的にキャッシュする
    • Router handlers における fetch は React Components ではないので同様の振る舞いとはならない点に注意
    • デフォルトで POST リクエストも自動的にキャッシュする点に注意
  • キャッシュの振る舞いは fetch の引数で変更できる: fetch('https://..., { cache: 'force-cache' })
  • キャッシュのパージには Time-base と On-demand の 2 つの方法がある
    • Time-base revalidation: キャッシュの有効秒数を {fetch('https://...', { next: { revalidate: 3600 } }) というかたちで引数で指定するか、export const revalidate = 3600` というかたちでルートセグメント単位で指定できる
    • On-demand revalidation: ページのパスレベルのキャッシュを revalidatePath メソッドにてパージするか、あらかじめ個別の fetch に対して fetch('https://...', { next: { tags: ['collection'] } }) というかたちで紐づけたタグレベルで revalidateTag メソッドにてキャッシュをパージできる
  • revalidate の際にエラーが発生した場合には過去のキャッシュが引き続き利用され、次回のリクエスト時にリトライされる
  • キャッシュのオプトアウトについては次のような手法が用意されている
    • The cache: 'no-store' is added to fetch requests.
    • The revalidate: 0 option is added to individual fetch requests.
    • The fetch request is inside a Router Handler that uses the POST method.
    • The fetch request comes after the usage of headers or cookies.
    • The const dynamic = 'force-dynamic' route segment option is used.
    • The fetchCache route segment option is configured to skip cache by default.
    • The fetch request uses Authorization or Cookie headers and there's an uncached request above it in the component tree.
  • Route Segment 配下(Layout, Page)の複数の fetch リクエストのキャッシュ設定を fetchCache セグメント設定オプションで一括で指定可能ではあるが推奨されてはいない

サーバーサイドでのサードパティライブラリの利用

  • prisma などのサードパティライブラリを利用してデータを取得している場合、Route Segment Config Option か React の cache 関数で cache と revalidation の制御が可能
  • データがキャッシュされるか否かは Route Segment のレンダリングが静的か動的であるかに依存する
    • 静的レンダリングの場合(デフォルト): リクエスト結果はキャッシュされ、Route Segment 単位で revalidate を行う
    • 動的レンダリングの場合: リクエスト結果はキャッシュされず、セグメントがレンダリングされるたびにリクエストが走る

ベストプラクティス

Next.js App Router では可能な限り Server Component によるサーバー上でのデータ取得が推奨されており、これにより以下ことが実現できる
  • (API などを介さず)データベースなどのリソースに直接アクセスできる
  • アクセストークンや API キーなどの機密情報をクライアント側にさらさずにすみ、よりセキュアな状態にできる
  • データ取得とレンダリングを同じ環境で行うことにより、クライアント-サーバー間通信とクライアント上での処理を低減できる
  • クライアント側からの複数の個別のリクエストをサーバー側における複数のデータ取得についての単一のリクエストにまとめることができる
  • クライアント-サーバー間の waterfalls を低減できる
  • リージョンによりデータフェッチ先をより近い場所にすることができ、結果として遅延が減少しパフォーマンスが向上する
  • Server Actions によりデータの操作や更新ができる
Data Fetching に関するベストプラクティスは下記のような内容となる
  • 必要な場所で必要なデータを取得する
    • コンポーネントツリーの中で同じデータが必要なコンポーネントが複数ある場合に、データをグローバルに取得したり props により受け渡したりする手法があるが、そのようなことをする必要はない
    • その代わりに fetch や React の cache を用いて必要な場所で必要なデータを取得してもパフォーマンス上問題が発生しないようになっている
    • これは fetchcache によるメモ化の振る舞いによるものとなる
  • loading.js と Suspense の活用
    • Suspense を活用することにより、必要なデータが取得できてないローディング状態の UI をまずはじめにレンダリングし、取得後にデータが格納された UI を表示することができる
    • これによりユーザーはページ全体のロードを待たずに UI の操作が可能となる
    • sequential data fetching が生じるパターンに対して、ページについては loading.js、コンポーネントについては Suspense が有効なプラクティスとなる
      • sequential data fetching: ある fetch が前の fetch の結果に依存しているパターン
      • parallel data fetching: 並列して fetch できるパターン
  • データを preload する
    • waterfalls を防ぐ他の方法として preload がある

Rendering

Server Components

Static Rendering と Dynamic Rendering の違い

  • Static Rendering
    • ビルド時か revalidation 時にレンダリングが走る
    • 結果はキャッシュされ、その内容は CDN にもキャッシュ可能
    • route が各ユーザーにパーソナライズされておらずビルド時に生成可能な、ブログ記事や商品ページなどに対して利用する
  • Dynamic Rendering
    • リクエスト時にレンダリングされる
    • データが各ユーザーにパーソナライズされている route やリクエスト時にしかわからない Cookies やクエリパラメータなどの情報を利用する場合に利用する
    • RSC ペイロードとデータキャッシュは別々にキャッシュされているため Dynamic Rendering に際してキャッシュされたデータを利用すること自体は可能であり、これにより Dynaic Rendering 時のパフォーマンスを改善できる

Caching

App Router におけるキャッシュ機構

  • Request Memoization
    • リクエスト単位で関数の返り値をキャッシュする
    • React Component tree 内で重複したリクエストに対してデータが再利用される
  • Data Cache
    • リクエストやデプロイ間にわたり永続してデータをキャッシュする
  • Full Route Cache
  • Route Cache

Styling

TODO

Optimizing

TODO

参考文献