問題
@Sicut_studyさんの【図解解説】話題の神Reactフレームワークreact-serverで技術記事投稿サイトを開発するチュートリアル【@lazarv/react-server/Convex/TypeScript】
をやっていて、レンダリング戦略が難しかったので、レンダリング方法と実装をまとめます。
レンダリング戦略について
1.クライアントサイドレンダリング(CSR)
- Reactのコードをクライアント(ブラウザ)で処理し、HTMLのファイルを生成する
- Reactはフレームワーク(Next.jsやreact-server)を利用しないとクライアントサイドでレンダリングされる
- クライアントのHTML生成が終わらないと表示されないため、初期画面表示が遅くなることがある
実装
src/components/LatestArticleList.tsx
import { api } from "../../convex/_generated/api";
import { useQuery } from "convex/react";
// 中略
// Reactコンポーネントは通常、クライアントサイドでレンダリングされる
export const LatestArticleList = () => {
// useQueryはConvexのフックであり、クライアントサイドでデータを取得するために使用される
const articlesData = useQuery(api.articles.get);
2. サーバーサイドレンダリング(SSR)
- Reactをサーバー側で処理をし、リクエストごとにHTMLを生成してクライアントに返して表示する
- SSRをすることで初期画面表示が早く、SEO対策(検索上位にサイトを表示する)やセキュリティ対策ができるようになる
- SSRを実現するためには、専用のWebサーバーを用意する必要がある
- リクエストごとにサーバーでページを生成するため、サーバーの負荷が増加し、パフォーマンスが低下する可能性がある
実装
src/app/api/articles/GET.popular.server.ts
import { ConvexClient } from "convex/browser";
import { api } from "../../../../convex/_generated/api";
const convexUrl = import.meta.env.VITE_CONVEX_URL;
if (!convexUrl) {
throw new Error("VITE_CONVEX_URL is not defined");
}
const client = new ConvexClient(convexUrl);
// request: Request は、サーバーサイドでHTTPリクエストを処理するためのオブジェクト
export default async function GetPopular(request: Request) {
const url = new URL(request.url);
const limit = url.searchParams.get("limit")
? Number(url.searchParams.get("limit"))
: 20;
// 特定のAPIエンドポイント(例: /api/articles/popular)に対するリクエストを処理
// サーバーサイドでのルーティングとデータ取得を行う典型的なパターン
const articles = await client.query(api.articles.getPopular, {
limit,
});
// サーバーサイドでのレスポンス処理
// HTTPレスポンスを生成して返している
return new Response(JSON.stringify(articles));
}
src/app/page.tsx
const getArticles = async () => {
const response = await fetch(
"http://localhost:3000/api/articles/popular?limit=10"
);
const data = await response.json();
return data;
};
// 中略
// getArticlesを叩いてデータを取得
const articlesData = (await getArticles()) as ArticleJson[];
3. SSG(StaticSiteGeneration)
- 静的サイト生成
- ビルド時に1度だけしかHTMLを生成しない
- SSRよりも初期画面が早く表示されてサーバー負荷も少ない
- 1度しか生成されないのでリアルタイム性は失われる
実装
react-server.config.mjs
// popularはSSG扱いとなる
export default {
root: "src/app",
export(paths) {
return [...paths, { path: "/popular" }];
},
};
4. ISR(Incremental Static Regeneration)
- SSR+SSGのような仕組みでビルド時にHTMLを生成
- それ以降はアクセスがあるたびにキャッシュ(revalidate)をチェックして期限が切れたら再度バックエンドから新しいHTMLをビルドして返す
- 静的に生成されたページを一定期間キャッシュする
(revalidate)をチェックし、その期間が過ぎると新しいリクエストが来たときにバックグラウンドでページを再生成
実装
src/app/popular/page.tsx
import { invalidate } from "@lazarv/react-server";
// 中略
const getArticles = async () => {
"use cache; ttl=200; tags=articles";
const response = await fetch("http://localhost:3000/api/articles/popular?limit=50");
const data = await response.json();
return data;
};
const refreshArticles = async () => {
"use server";
// refreshArticlesが呼ばれたら、invalidate関数を使用してキャッシュを手動で無効化
// リクエスト時に新しいデータを取得する
invalidate(getArticles);
};
終わりに
レンダリング戦略も色々なやり方があることがわかり、勉強になりました。
参考
【図解解説】話題の神Reactフレームワークreact-serverで技術記事投稿サイトを開発するチュートリアル【@lazarv/react-server/Convex/TypeScript】