Open10

Next.jsのAPP Routerメモ。Pages Routerとの変更点など。

k_neko3k_neko3

サーバーコンポーネントとクライアントコンポーネント

とりあえずAPP Routerを使うとコンポーネントはデフォルトでReact Server Componentsになるらしい。

サーバーコンポーネントではできない機能を持つクライアントコンポーネントを作る場合は、自分でクライアントコンポーネントだよと記述する必要がある。

https://nextjs.org/docs/app/building-your-application/rendering/server-components

https://nextjs.org/docs/app/building-your-application/rendering/client-components

どういう時にどちらを使えば良いのか

https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns

  • サーバーコンポーネントを使う時
    • データの取得(fetch関数とか)
    • バックエンドに直接アクセスするとき
    • 機密情報(APIキーとか)をサーバーに保存するとき
    • サーバーへの依存度を高く保つ(英訳が難しいけど、できるだけサーバーで処理したい場合ってことだと思う)
    • クライアントサイドのJavaScriptを減らす
  • クライアントコンポーネントを使う時
    • イベントリスナー
    • useStateとかのフック
    • ブラウザでだけ使えるAPI
    • useStateとかを使うカスタムフック
    • Reactのクラスコンポーネント

ブラウザでユーザーが何かしたら反応するものや、ブラウザからでないとわからないものに関係するものは、クライアントコンポーネントになる、と思えば良さそう。

参考

https://qiita.com/getty104/items/74d975ff02bdf4fa9b2b

https://qiita.com/naruto/items/c17c79ec5c2a0c7c4686

https://zenn.dev/forcia_tech/articles/202305_next_js_rsc

k_neko3k_neko3

データ取得

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating

データ取得は基本的に自分でasync functionを作って、ローカルファイルを開いたり、fetchを使って外部APIを叩いたりする。

で、何もしなければ「Static Site Generation(getStaticProps)」になるが、キャッシュのオプション設定で { cache: 'no-store' }とすると「Server-side Rendering (getServerSideProps)」になる。

Pages RouterのgetStaticPropsとかgetServerSidePropsは、ページとして表示されるファイル内(最上位のindex.tsxとか)でしか使えず、propsをコンポーネント間でバケツリレーする必要があったが、App routerではコンポーネント単位でデータを取得することができる。で、さらに自動で重複が避けられるようになっている、らしい。

以下のキャッシュに仕組みが書いてある。

https://nextjs.org/docs/app/building-your-application/caching#request-memoization

キャッシュの設定と再検証(Revalidation)の設定で、どうやってキャッシュするか、キャッシュの再検証をどうするかを決めることで、以前のISRとかを実現できるようになっている。

// SSR
// キャッシュはされずにリクエストごとにデータ取得
const res = await fetch(`https://...`, { cache: 'no-store' })

// SSG
// ビルドするときにキャッシュされてそのまま
const res = await fetch(`https://...`)

// ISR
// ビルド時キャッシュ、revalidate秒後に最初のアクセスがあると、データ取得
// revalidate秒後の2回目のアクセスから新しいキャッシュのデータを表示
const res = fetch('https://...', { next: { revalidate: 3600 } })

//On-Demand Revalidation
// ビルド時キャッシュ、指定した条件を満たしたアクセスがあるとキャッシュをパージ
const res = await fetch('https://...', { next: { tags: ['collection'] } })

fetchのオプションは以下。

https://nextjs.org/docs/app/api-reference/functions/fetch

k_neko3k_neko3

動的パス

https://nextjs.org/docs/app/api-reference/functions/generate-static-params

Pages RouterのgetStaticPathsは、APP RouterではgenerateStaticParamsになった。

ぱっと見、基本的な使い方は変わってなさそうだけど、細部は変わっているように見える。

pgae.tsxだけでなく、layout.tsxでも使えるとか。

大きく違うのはgetStaticPathsの内部で使っていたfallbackか。

dynamicParamsというRoute Segment オプションで設定するらしい。falseだとgenerateStaticParamsで定義したurl以外は404をかえす。

export const dynamicParams = true // true | false,

https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams

k_neko3k_neko3

Tailwind CSSの設定追加

tailwind.config.jsに設定を追加する必要がある。

module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Add this line
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}
k_neko3k_neko3

ルーティングフック

useRouterが結構変わっている。

https://nextjs.org/docs/app/api-reference/functions/use-router

import { useRouter } from 'next/navigation'

next/navigationから呼ぶようになった。前はnext/routerだった。

以前のpathnameに似た機能は独立したフックのusePathname()になっていたり。queryuseSearchParams()になっていたり。

https://nextjs.org/docs/app/api-reference/functions/use-pathname

https://nextjs.org/docs/app/api-reference/functions/use-search-params

k_neko3k_neko3

SSGで外部APIにfetchしてて、revalidateしてるというのが前提。その、revalidate後の外部API接続時にフェッチエラーになったら、サイトの表示はどうなるのか、という疑問。

予想としては表示が変わらずそのまま、なんだけど、本当にそうなるのか試してみたところ、そうだったというだけのメモ。

ただ、キャッシュがどうなっているのかとか良くわからない。

フェッチでrevalidateにしている場合、フェッチデータに最初にアクセスすると200、次にアクセスするとステータスコードが304になって、キャッシュにつながる。

で、revalidate時間が過ぎると、裏側でフェッチしてキャッシュのつながり先を更新してくれるということだと思うのだけど、そこでフェッチエラーが出た場合はキャッシュは据え置きっぽい。

挙動を見る限り、304でキャッシュにつながって、サイトでの表示はされるので、多分そういうことっぽい。

以下の画像だと、キャッシュのデータに「stale」とあって、古いということを判定しているだけで消えてないっぽい。

https://nextjs.org/docs/app/building-your-application/caching#time-based-revalidation

気になったのがオンデマンドほう。

「purge」になってるので、キャッシュ消してるってことよね。

https://nextjs.org/docs/app/building-your-application/caching#on-demand-revalidation

オンデマンドのリクエストがあると、「purge」して、次にアクセスしてもキャッシュは「miss」になって、フェッチ先にデータを取りに行くのがオンデマンド。

手もとで試してないから以下は想像だけど。

この時のフェッチでエラーになったらerror.jsが起動する、で良いのかな。多分そうなりそうな気がするけど、今度また試してみよう。

k_neko3k_neko3

https://nextjs.org/docs/app/api-reference/components/link

Next.jsのLinkコンポーネントを使うとプリフェッチされることで、ページ遷移するときに読み込みが早くなって快適というのが、いいことの一つだけれど。

リンク先のページでフェッチを使ってると、プリフェッチが働いてフェッチも動いてるのよね、多分。chromeで見てたら、アクセスしたページでは使わないfetchのアクセスで304がいっぱい出てたし、多分そう。

SSGでrevalidateを使っている時に、全ページに、Linkコンポーネントでリンクを貼っていれば(例えばナビに全部リンクはっとくとか)、再検証設定時間後に、どこか1ページにアクセスがあれば全てのfethcの再検証が動作するわけで、それはそれで便利だなと思った。アクセスが少ないサイトでも、cron jobでどこかのページに一回アクセスしとけばよいわけで。

問題としては、SSRとかの時は困りそう。プリフェッチで毎回APIを叩かれるわけで、アクセス数が多いサイトの場合はすごいことになりそう。

個人で作る分には全然気にならないけど、大規模サイトだとキャッシュ戦略って大変そうだなと思ったり。SSGとSSRが複雑に絡み合うサイトとかになるのかな。管理大変そう。

SSRとSSGのrevalidateを併用すると。fetchがどんな感じになるのか今度試してみよう。

App Routerでは複数のfetchを勝手に裏でまとめてくれて上手いこと処理してくれるとなっているけど、いろんな場所のいろんな階層のコンポーネントに違う設定でfetchが使われていたらどうなるのか。

そういう複雑なことにならないように設計するのが一番いいんだろうなとは思うけど。