Open2
学び:Next.js
useRouter()
App Router
を使用している場合、next/router
ではなくnext/navigation
を使う。
useRouter()
はカスタムコンポーネントなので、'use client'
を付ける。
'use client'
import { useRouter } from 'next/navigation'
export default function BackButton() {
const router = useRouter()
return (
<button onClick={() => router.back()}>
もどる
</button>
)
}
Middleware
概要
/quiz/tag/**
にアクセスされたら、/quiz/tag/${tag}/p/1
へリダイレクトするコードを実装したときのメモ。
実装
tag階層以下に動的にページ分割ページを実装した。tag階層のトップディレクトリは特に必要ないのでアクセスされたらページ分割の最初のページに移動する挙動を仕様として決定。それを実現する方法としてNext.jsのMiddlewareにて以下コードを実装した。
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl; // リクエストされたURLを取得
const segments = pathname.split('/'); // パスをスラッシュで分割
const tag = segments[segments.length - 1]; // 最後のセグメントをタグとして取得
// パスが/quiz/tag/{tag}形式で、ページ番号が指定されていない場合にリダイレクト
if (segments.length === 4 && segments[2] === 'tag' && tag && tag !== 'p') {
// /quiz/tag/{tag}形式のURLにページ番号を追加してリダイレクトする
return NextResponse.redirect(new URL(`/quiz/tag/${tag}/p/1`, request.url));
}
return NextResponse.next(); // それ以外の場合は通常の処理を続行
}
export const config = {
// タグページのURLをマッチングする
matcher: "/quiz/tag/:tag*",
};
気になること
- サーバーにどの程度負荷がかかるのか?静的ページとしてビルドするなら負荷が低減するのか?
- Vercelなどキャッシュが効く環境であれば負荷は軽いのか?
AIに解説してもらった。
Next.js Middlewareコード解析
以下は、提供いただいたNext.jsのミドルウェアコードの行ごとの解析と詳細な解説です。コードの各ステップで何を実行しているのか解説します。
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
-
NextResponse
とNextRequest
をnext/server
からインポートしています。-
NextResponse
: ミドルウェアやAPIルートでレスポンスを生成するためのクラスです。 -
NextRequest
: リクエストの情報を格納するクラスで、リクエストURLやヘッダー、クエリパラメータなどにアクセスするために使用します。
-
export function middleware(request: NextRequest) {
-
middleware
関数をエクスポートしています。- この関数はミドルウェアとして動作し、
NextRequest
型のリクエストオブジェクトを受け取ります。 -
middleware
関数はURLのチェックやリダイレクトなどの処理を行い、リクエストが続行するかリダイレクトされるかを決定します。
- この関数はミドルウェアとして動作し、
const { pathname } = request.nextUrl; // リクエストされたURLを取得
- リクエストされたURLのパス部分(pathname)を取得します。
-
request.nextUrl
はURLオブジェクトであり、その中のpathname
を使用してリクエストされたパスを取得しています。 - 例えば、URLが
/quiz/tag/javascript
の場合、pathname
は/quiz/tag/javascript
となります。
-
const segments = pathname.split('/'); // パスをスラッシュで分割
-
pathname
を/
で分割し、配列segments
に格納します。- これにより、パスをセグメントごとに分割し、各セグメントに簡単にアクセスできるようにしています。
- 例えば、
pathname
が/quiz/tag/javascript
の場合、segments
は["", "quiz", "tag", "javascript"]
になります。
const tag = segments[segments.length - 1]; // 最後のセグメントをタグとして取得
-
segments
配列の最後の要素をtag
として取得します。-
segments.length - 1
を使って、配列の最後の要素を取得します。 - これは
/quiz/tag/javascript
の場合、tag
は"javascript"
となり、タグとして使用されます。
-
if (segments.length === 4 && segments[2] === 'tag' && tag && tag !== 'p') {
- リダイレクト条件を指定するif文です。
-
segments.length === 4
: セグメントが4つであることを確認します(例:/quiz/tag/javascript
)。 -
segments[2] === 'tag'
: 第3のセグメントが"tag"
であることを確認します。 -
tag && tag !== 'p'
:tag
が存在し、"p"
でないことを確認します。 - これらすべての条件が満たされた場合に、リダイレクト処理が行われます。
-
return NextResponse.redirect(new URL(/quiz/tag/${tag}/p/1, request.url));
- リダイレクトを実行し、ページ番号を追加します。
-
/quiz/tag/${tag}/p/1
という形式のURLを生成し、ページ番号1
を付加したURLへリダイレクトします。 -
new URL()
はリダイレクト先のURLを生成するために使われ、request.url
でベースURLを指定しています。
-
return NextResponse.next(); // それ以外の場合は通常の処理を続行
- それ以外のリクエストについては通常の処理を続行します。
-
NextResponse.next()
は処理を次に進めるメソッドで、リクエストがリダイレクトされずにそのまま次のミドルウェアやルートハンドラに渡されます。
-
export const config = {
// タグページのURLをマッチングする
matcher: "/quiz/tag/:tag*",
};
- ミドルウェアの設定オブジェクトをエクスポートしています。
-
matcher
プロパティにより、特定のパスに対してのみミドルウェアが適用されるように指定します。 -
"/quiz/tag/:tag*"
で指定されているため、/quiz/tag/
に続くパスに対してこのミドルウェアが適用されます。
-
このコードは、特定のURLパターンにマッチした場合にリダイレクトを行うためのミドルウェアです。リクエストされたURLのパスに基づいて、必要に応じてリダイレクト先にページ番号を追加する動作を実現しています。