パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter(fetch)

はじめに

こんにちわ!こんばんわ!ご観覧ありがとうございます。
定期的に雑色な記事を投稿します。(タイトルは記事のメインの内容にしてますが)
基本的には抱えている問題を改善し、改善方法を記事にしてアップしようと考えています。

お題と経緯

NextjsのPageRouterからAppRouterに乗り換えてみてパフォーマンスを測ってみたいし、使ってもみたいなってことで、
今回はFirebaseのhostingでAppRouterがどこまで機能するかを、パフォーマンス向上の一環として試そうと思います!
まずはAppRouterでサンプルのAppを作成しつつ、備忘録で説明を書いていこうと思います。

今回の内容

AppRouterで色々なfetchしていきたいと思います!

使用した技術

Category     Technology Stack
Frontend Nextjs,React,Typescript
Backend Typescript,Node,CloudFunction
Database Firestore
Design   XD  

前回の内容

前回記事

パフォーマンス無関心で作ったサイトのパフォーマンスチューニング番外編 Nextjs AppRouter - zare926のブログ

NextjsのAppRouterで静的コンテンツを表示するところまでやってみました。

AppRouterでfetchする

SGやだとgetStaticProps、SSRだとgetServerSidePropsを定義し、propsでJSXに値を渡していましたが、AppRouterのfetchはJSX内に直接書くことができます。

import { Squares } from "@/components/Squares";
import Link from "next/link";

export default async function Home() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1"); // ここ
  const data: { name: string } = await response.json();

  return (
    <div className="p-8 relative">
      <h1 className="text-gradient-blue-green" data-content="TopPage">
        TopPage
      </h1>
      <Link href="/sample" className="text-sky-600">
        Sampleページへ
      </Link>
      <p>{data.name}</p> // 定義したデータをそのまま使える
      <Squares />
    </div>
  );
}

バケツリレーがなくなりますね。
ちなみにこの実装だとSGになり、ビルド時に1回だけfetchをしてくれます。

サーバーコンポーネントでSG,SSR,ISRの書き方

SSRの場合は以下の様に書きます。

import { Squares } from "@/components/Squares";
import Link from "next/link";

export const fetchCache = "default-no-store";

export default async function Home() {
  const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${number}`
  );
  const data: { name: string } = await response.json();
  return (
    <div className="p-8 relative">
      <h1 className="text-gradient-blue-green" data-content="TopPage">
        TopPage
      </h1>
      <Link href="/sample" className="text-sky-600">
        Sampleページへ
      </Link>
      <p>{data.name}</p>
      <Squares />
    </div>
  );
}

build後もサーバーサイドでやり取りしてくれるので、動的な表示が可能になります。
他にも、Dynamic Functionというものがあり、Server Component 内で使える cookies(), headers()のことを指します。
cookies(), headers()を定義するとSSRになります。

import { Squares } from "@/components/Squares";
import Link from "next/link";
import { cookies } from "next/headers";

// export const fetchCache = "default-no-store";

export default async function Home() {
  const cookieStore = cookies();
  const number = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${number}`
  );
  const data: { name: string } = await response.json();

  return (
    <div className="p-8 relative">
      <h1 className="text-gradient-blue-green" data-content="TopPage">
        TopPage
      </h1>
      <Link href="/sample" className="text-sky-600">
        Sampleページへ
      </Link>
      <p>{data.name}</p>
      <Squares />
    </div>
  );
}

ISRの説明をするためにapiを用意。
src/app/sample-date-get/route.ts

import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ data: new Date().toISOString() });
}

時間でCacheの設定をするのでわかりやすいAPIを準備します。

import { Squares } from "@/components/Squares";
import Link from "next/link";

export const revalidate = 10; // ここ

export default async function Home() {
  const response = await fetch(`http://localhost:3000/sample-date-get`);
  const data = await response.json();

  return (
    <div className="p-8 relative">
      <h1 className="text-gradient-blue-green" data-content="TopPage">
        TopPage
      </h1>
      <Link href="/sample" className="text-sky-600">
        Sampleページへ
      </Link>
      <p>{data.data}</p>
      <Squares />
    </div>
  );
}

上記の場合はコンポーネント全体で10秒キャッシュとしています。
もしくはfetch関数単位でも設定可能です。

  const response = await fetch(`http://localhost:3000/sample-date-get`, {
    next: { revalidate: 10 },
  });

こちらに関してyarn devだとうまくいきますが、yarn buildだとうまくいきません・・・、原因がわかったら記事に追加しておきます。
.next/cache/fetch-cache ここにcacheで下記の様にjsonができているので、機能して欲しいのですが・・・

"status":200,"url":"http://localhost:3000/sample-date-get"},"revalidate":10,"tags":[]}

最後に

少しクセはありますが、cacheが楽に書けるなと思いました。
またページによってSG、ISRなど機能を振り分けしやすいと感じます。
ただISRだけはbuild後、期待通りのcacheではないので、調べないと・・・
果たしてFirebaseでhostingはできるのか・・・?
次回はクライアントコンポーネントから動的な関数を呼ぶ方法を書いてみたいと思ってます。
ここまでみていただきありがとうございました!!!