Next.js app router のドキュメント読む

Getting Started
これからプロジェクトを作成する場合は app router がおすすめよ
app/
作成して layout.tsx
, page.tsx
作成すればそれがトップページになる
layout.tsx
忘れた場合、自動で作成されるよ
オプションだけど public/
フォルダーに画像やフォントなどの静的アセットいれな
app/
下のコードから参照できるよ
プロジェクトストラクチャー にフォルダーやファイルの機能が載っている
この後の Routing で説明してくれそうなので、いったん飛ばして最後に戻ってくる

Routing
今後の用語は以下に従う
Tree, Subtree, Leaf, Root, Segment を覚えれば良さそう
ファイル名の規則は以下になる
名前 | 機能 |
---|---|
layout | Segment とその子供のレイアウト設定ファイル |
page | Root 独自の UI ファイル |
loading | Segment とその子供の Loading 時のファイル? Suspense で自動 fallback? |
not-found | Segment とその子供の NotFound 時のファイル 表示方法は? |
error | Segment とその子供の Error時のファイル 表示方法は? |
global-error | グローバルエラー時の UI グローバルエラーとは? |
route | サーバーサイドの API エンドポイント |
template | 特別に再レンダリングされたレイアウト UI ? |
default | Parallel Routes のフォールバック UI |
わからんやつあるけどリンクがあるので、今後説明出てきそう
上記以外の名前のファイルも app/
下に作成できるよ
Segment を深くする場合はフォルダーをネストして作成(page route と同じ)
app/
下フォルダーを作成しても page.js
を作成しなければルーティングされない
Layout
レイアウトファイルはその Segment とその子供で共有され(デフォルト)、再レンダリングされない
Route Groups を使用すると共有レイアウトの範囲を設定できるらしい
デフォルトではサーバーサイドコンポーネント
/app
のトップにレイアウトファイルは必須で、html
, body
が必須
Templates
Templates はレイアウトファイルと似ているが、新しいインスタンスを作成する
なので、Templates を使用しているページを開くと DOM が再作成され、ステートは保持されず、エフェクトが再同期される
useEffect
やuseState
に依存する機能
レイアウト内で Suspense を定義した場合、root を初めて開いたときに表示されるが、ページを切り替えるときは表示されない
Templates に定義すると各ナビゲーションで表示される

Linking and Navigating
<Link>
は <a>
タグの拡張で、ナビゲーションに使えるよ
usePathname()
を使用すると、現在の URL のパス名を読み取ることができる
ナビゲーションで特定の箇所まで行きたい場合は、#
をつけてな
useRouter
でナビゲーションの push や replaceなどができるで (React router に似ているはず)
<Link>
タグがビューポートに表示されるとプリフェッチされるで
router.prefetch
でもプリフェッチすることができるで
基本 <Link>
で良さそう?
プリフェッチも静的ルートと動的ルートで挙動が分かれる
静的ルート:デフォルトで true, ルート全体がプロジェクト&キャッシュ
動的ルート:デフォルトは automatic, loading.js ファイルまでの共有レイアウトのみがプリフェッチされ、30秒間キャッシュされる
Segment の子供間でナビゲートする場合親 Segment の Layout は保持されている
Next.js ではソフトナビゲーションを使用しておりナビゲーションによって、ページ全体がリロードされることはない

Route Groups
フォルダー名をかっこで囲むことでルートグループを作成できる(folderName)
それぞれのルートグループで、異なるレイアウトを適応できるで
複数のルートグループを作成する場合は Top の layout.js
削除して、それぞれのルートグループで layout.js
を作成してな
<html>
, <body>
を忘れずに
ルートグループまたがるとページ全体がロードされるで

Dynamic Routes
動的セグメントは角括弧で囲むとできるで [folderName]
これは Page Router と変わらない気がする
generateStaticParams
を使用するとリクエスト時ではなくビルド時に静的ルートできる
動的セグメント内で自身を設定するよう
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug, // ここで設定
}))
}
[...folderName]
を使用すると丸っとセグメントを取得できるで
app/shop/[...slug]/page.js
→ /shop/a/b/c
→ { slug: ['a', 'b', 'c'] }
[[...folderName]]
を使用すると上記の /shop
も一致するパスに入るで

Loading UI and Streaming
loading.js
は Suspense を使って Loading 中の UI を定義できるで
Suspense と組み合わせるのと思ったが、自動で挿入されるっぽい
独自の Suspense を定義することも可能だよ
Suspense を使用したストリーミングをサポートしてるからね
ストリーミングとは?
その前に SSR の挙動な
- 初めにサーバー側でページに必要なデータ取得するで
- そのデータ使って HTML をレンダリングするで
- HTML, CSS, JS がクライアントに送られるで
- 良しなに hydrates するで
この流れの時1が重要になる
データがすべて取得できないと HTML 作成できないからな
ストリーミングは、ページの HTML を小さなチャンクに分割し、それを徐々に送信できるんや
そのおかげですべてのデータを取得するのを待たたずに、ページの一部が表示できるんや
ストリーミングはコンポーネントモデルと相性がいい
Header などはデータを待たずに送信できるし、コンポーネントの送信に優先順位をつけることもできる
上記を実現するのが <Suspense>
や
詳しくは React のドキュメントを読んでな

Error Handling
こちらもセグメント内に error.tsx
を定義すれば Error Boundary で自動的にラップされる(便利ー)
エラー境界より上のレイアウトはその状態を保持されるで
reset()
を使用するとエラー境界の内容の再描画を実行することができるで
error は layout の下に位置するので、エラーが起きた場合 Layout を保持したままエラー表示するで
つまり Layout でのエラーはキャッチできないんや
トップレベルのエラーを処理したい場合は、global-error.js
を作成してな
<html>
, <body>
を忘れないでね

Parallel Routes
Parallel Routes を使用すると、複数のページを同じレイアウト内でレンダリングできるんや
そうすることで、ルートごとに Loading や Error 管理ができるんや
また条件付きレンダリングもできて 同じ URL で完全に分離されたコードが有効になるんや
ParalelRoutes は @folderName
の名前で定義でき、同じセグメントのレイアウトに渡されるんや
@folderName
はスロットって呼ばれるで
このスロットは URL 構造に影響しない
実は app/page.js
は app/@children/page.js
なんや
だからレイアウトファイルで children が使えるんやな
URL に基づいてスロットのアクティブ状態?を回復できない場合、default.js
に定義された UI をレンダリングするで
それが利用できない場合は 404 がレンダリングされるで

Intercepting Routes
Intercepting Routes を使用すると、ユーザーが別のコンテキストに切り替えることなく、ルートのコンテンツを表示できる
は?
モーダルと一緒に用いることで、モーダル上で新たなページが表示できるよう
(..)folderName
で定義する

Route Handlers
route.js
を定義すると、カスタムリクエストハンドラーを作成できる
API リクエスト用?
なので page.js
と同じ階層に定義できない
api/
下に定義するのが良さそう?

Middleware
Middleware を使うと、リクエストが完了する前にコードを実行することができる
送られてきたリクエストに基づいて、レスポンスを書き換えたり、リダイレクトしたり、リクエストやレスポンスのヘッダーを変更したり、直接レスポンスしたりすることで、レスポンスを変更することができる
プロキシサーバー的な?
Middleware はプロジェクト内のすべてのルートに対して呼び出される
- next.config.js の headers
- next.config.js の redirects
- Middleware
- next.config.js の beforeFiles
- Filesystem routes
- next.config.js の afterFiles
- Dynamic Routes
- next.config.js の fallback
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
matcher は正規表現に対応している
NextResponse API を使用するとリダイレクトやレスポンスの書き換え
クッキーやヘッダーの設定ができる
また Middleware から直接レスポンスを返すこともできるで
サーバーへのリクエストを軽減できるんとちゃうか
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}

Project Organization and File Colocation
page.js
や route.js
を定義しない限り公開されないので、好きにフォルダーやファイルを作成できるで
_folderName
にすればプライベートフォルダーになって、page.js
を定義しても公開されへんで
基本的にはページ以外のフォルダーを作成したい場合、プライベートフォルダーにしておくのが良さそう?
Next.js の将来的な名前衝突も避けられそうやしな
例見たらそんなことなかった
普通に componetsフォルダー作成してた

Data Fetching
Next.js はネイティブの fetch API を拡張してるで
自動的にメモするからな
ただ Route ハンドラー(route.js)の fetch はメモされないで、React のコンポーネントツリーでないからな
cokies や headers にも便利な関数用意してるで
デフォルトでは fetch
は自動でキャッシュされるで
POST
もキャッシュするからな
Route ハンドラーは別や
再リクエストするには2つの方法があるで
fetch 関数に revalidate を設定すれば、その秒数経ってたら再リクエストするで
fetch('https://...', { next: { revalidate: 3600 } })
他に revalidatePath
や revalidateTag
を使って、変わってたら再リクエストするで
React Query みたいな感じ?
cach: 'no-store'
に設定したりすることでキャッシュを無効にできるで