- Next.js のルーティングの考え方、流れが知りたい!
このような疑問にお答えします。
Next.js には、以下の二通りのルーティング方法が用意されています。
- ファイルシステムベース
- App Router
本記事ではこの二つを掘り下げて解説します。
【基本】ファイルシステムベースのルーティング
この方法では、pages
ディレクトリ内のファイル名がそのまま URL パスになります。
基本的なルーティング
一般的なルーティングは、以下のように考えます。
ディレクトリ構造 | URL のパス |
---|---|
pages/index.tsx | / |
pages/about.tsx | /about |
index.tsx
だけがサイトのホームページを表す特別なファイル名になります。
そのほかは、ファイル名がそのまま URL パス名になる形です。
動的ルーティング
ファイル名やディレクトリ名を[param]
の形式にします。
例えば以下の通りです。
ディレクトリ構造 | URL のパス |
---|---|
pages/posts/[id].tsx | posts/1 など |
pages/user/[id].tsx | user/1 など |
上記の場合にはid
ごとに異なるページを生成できるようになります。
ネストされたルーティング
ディレクトリをネストすると、URLのパスをネストできます。
ディレクトリ構造 | URL のパス |
---|---|
pages/blog/2020/posts | /blog/2020/post |
キャッチオールルーティング
ファイル名を [...param]
の形式にすると、パスをパラメータとして受け取ることができます。
ディレクトリ構造 | URL のパス | 受け取る値 |
---|---|---|
pages/posts/[...slug].tsx | /posts/a | ['a'] |
/posts/a/b | ['a', 'b'] | |
/posts/a/b/c | ['a', 'b', 'c'] |
実践的にはページ内を検索する際などに使われます。
URL が/products/electronics/cameras/compact-digital-camera だった場合には、pages/products/[…category].tsx で一括して処理できます。
URL パスを見ただけでどのような内容かがはっきりするので、SEO 上でも有利とされています。
APIルート
pages/api
ディレクトリ内にファイルを作ると、API エンドポイントを作成できます。
apiディレクトリ内はサーバーサイドのみで実行され、クライアントからは直接アクセスできるエンドポイントが提供される形です。
【アドバンス】App Router を使ったルーティング
Next.js 13 以降では、「App Router」という方法も使えるようになりました。
これは前述のファイルベースのルーティングを拡張するもので、アプリケーション内でのルート間のナビゲーションやデータフェッチングをより柔軟に管理できるようにするものです。
ざっくりと分類すると、以下のようになります。
- ルートレベルでのデータフェッチ
- レイアウトの継承
- クライアントサイドの状態の維持
- モジュラーなページ構造
それぞれ、解説します。
ルートレベルでのデータフェッチ
App Router を使用するとページ遷移が発生するたびにサーバーから必要なデータを自動的に取得してページに提供します。(ルートレベルでデータフェッチを行う)
具体的には、App Routerにおいて、各ルートコンポーネントで直接データを取得し、それをページコンポーネントに渡す流れです。
実装の流れとしては、通常のReactの機能と同様に非同期関数を使います。具体的にはloader
関数で実装され、この関数はサーバーサイドで実行されることになります。
loader
関数がURLから必要なパラメータを取得- APIを呼び出してデータをフェッチ
具体的なコードは以下の通り。
export async function loader({ params }) {
// データ取得用のAPIを呼び出し
const response = await fetch(`https://api.example.com/data/${params.id}`);
const data = await response.json();
return { props: { data } }; // propsを通じてコンポーネントにデータを渡す
}
取得したデータは props
を介してルートのコンポーネントに渡されます。
レイアウトの継承
App Routerでは「レイアウトの継承」を非常に簡単に実装できます。
これにより、コードの重複を減らし、メンテナンスの効率を向上させながらアプリケーション全体に一貫したレイアウトを適用できるようになります。
簡単な実装の流れは以下の通りです。
- レイアウトコンポーネントの作成
- レイアウトを適用するページの作成
以下、具体的に解説します。
1. レイアウトコンポーネントの作成
共通のヘッダーやフッター、ナビゲーションバーなど、ページ間で共有される要素を含むレイアウトコンポーネントを作成します。
// components/Layout.js
import React from 'react';
const Layout = ({ children }) => {
return (
<div>
<header>ヘッダー</header>
<main>{children}</main>
<footer>フッター</footer>
</div>
);
};
export default Layout;
2. レイアウトを適用するページの作成
ルートディレクトリにあるコンポーネントでレイアウトをインポートし、その内部にページ固有のコンテンツを配置します。
// pages/about.js
import Layout from '../components/Layout';
const AboutPage = () => {
return (
<Layout>
<p>これはAboutページです。</p>
</Layout>
);
};
export default AboutPage;
この方法では、Layout
コンポーネントがすべての子ページに対して共通のレイアウトを適用します。
ページを追加するときは、単純に Layout
をインポートした上で、その中に新しいコンテンツを配置するだけです。
クライアントサイドの状態の維持
以下のようなデータをクライアントで保持できます。
- ページ間でのデータ
- UIの状態
シングルページアプリケーション(SPA)では特に有効です。
以下に React の Context API を使った実装例を示します。
import React, { useContext, useState } from 'react';
// ステートを管理するコンテキストを作成
const AppStateContext = React.createContext(null);
// コンテキストプロバイダコンポーネント
function AppStateProvider({ children }) {
const [state, setState] = useState({ user: null });
return (
<AppStateContext.Provider value={{ state, setState }}>
{children}
</AppStateContext.Provider>
);
}
// 利用例
function UserProfile() {
const { state, setState } = useContext(AppStateContext);
function loginUser() {
setState({ user: { name: 'Yamada' } });
}
return (
<div>
<p>ユーザー名: {state.user ? state.user.name : 'ゲスト'}</p>
<button onClick={loginUser}>ログイン</button>
</div>
);
}
// アプリケーション内でプロバイダを適用
function App() {
return (
<AppStateProvider>
<UserProfile />
</AppStateProvider>
);
}
この例ではユーザーのログイン状態を保持し続けるため、ユーザー情報をContext
で管理しています。
仕組みとしては以下の通りです。
- グローバルステート管理ツールの使用
- URLパラメーターの利用
- ローカルストレージやセッションストレージの利用
- クライアントサイドのキャッシュ
それぞれ解説します。
1. グローバルステート管理ツールの使用
ReduxやContext APIなどのステート管理ライブラリを使用して、アプリケーション全体で状態を一元管理します。これにより、ページ遷移しても状態が保持されます。
2. URLパラメーターの利用
Next.jsの動的ルーティング機能を利用して、URLにパラメーターを含めることで状態を管理します。これはフィルタリングや検索クエリなどに有効です。
3. ローカルストレージやセッションストレージの利用
ブラウザのストレージ機能を利用して、クライアントサイドでのデータを保存し、ページリロード後も状態を維持できます。
4. クライアントサイドのキャッシュ
Apollo ClientやReact Queryのようなデータフェッチングライブラリを使用し、取得したデータをクライアントサイドでキャッシュすることで、サーバーへの不要なリクエストを減らし、状態の維持を助けます。
モジュラーなページ構造
モジュラーな構造とは、ページやコンポーネントを小さな単位で独立させてそれぞれが単一の機能や目的に集中する設計を指します。
例えば、products
やusers
のようなディレクトリを作成し、それぞれに対応するページと API ルートを管理します。
- pages
- products
- [id].js // 個別の商品ページ
- index.js // 商品一覧ページ
- users
- [id].js // ユーザーのプロファイルページ
- index.js // ユーザー一覧ページ
ルートディレクトリを機能単位で分割し、各ディレクトリに必要なコンポーネントとデータローディングのロジックを含めます。
各モジュールの役割は以下の通りです。
- ページコンポーネント:
- ユーザーに表示されるページのメインコンテンツを担当。データの表示やユーザー入力の処理などを行う。
- APIルート:
- サーバーサイドでデータの取得や更新を行うAPIエンドポイント。これにより、フロントエンドとバックエンドのロジックが明確に分離される。
- 共通コンポーネント:
- 複数のページで共通して使用されるUI要素、例えばナビゲーションバー、ヘッダー、フッターなど。
まとめ:ルーティングを理解して開発の基礎を身につけよう
Next.js のルーティングの基礎を解説してきました。
アプリケーション開発でルーティングを理解することは基礎になりますので、ぜひ本記事を参考にマスターしてみてください。
コメント