🙄

CSR SSR SSG ISRを少し理解できた。

2024/02/22に公開

Next.jsを使うためにレンダリングを理解する。備忘録

どのレンダリングパターンにおいても、

この順は変わらない。
それぞれのレンダリングパターンの特徴を理解して開発環境に合わせて利用する。

CSR (クライアントサーバーレンダリング)

特徴

1.クライアントからサーバーへ要求

・クライアントからサーバーへページの読み込みなどのreqがあると、サーバーは骨組み状態のHTMLをresする。

2.HTMLの要求とリソースを提供

・クライアントでHTMLを上から順に解析する。
・cssやscriptがあればそのリソースをreqしサーバーからresされる。

3.JavaScriptの実行と仮想DOM

・受け取ったJavaScriptを実行する。

・Reactであれば、

import React from "react";
import ReactDOM from "react-dom";

const App = () => {
return(
 <div>
<h1> Hello World! </h1>
 </div>

);
};

ReactDOM.render(<App/>,document.getElementById('app'));

・このApp関数をHTMLファイルのbodyにあるapp要素に入れるという流れ。
・コンポーネントは実際には仮想DOM上に作成され、ReactDOM.renderメソッドを通じて実際のDOMに挿入される。

メリット

・SPA(シングルページアプリケーション)に最適。
・ページ移動のストレスが軽減される
・開発がシンプルな構成となる。

デメリット

・初回はページの読み込みが遅くなる。
・クライアントの性能に依存してしまう。
・情報更新が多いWebアプリには不向き
・SEOが弱くなりやすい
→Googleが骨組みのHTMLを判定してしまうため。

採用が多い例

・Google MAP
・Trello

UI・UX の観点からも操作性が良いためページ偏移の少ないアプリで採用されることが多い。

SSR (サーバーサイドレンダリング)

特徴

サーバーでオンデマンドにプリレンダリングされている

・クライアントからの要求にサーバーであらかじめHTML,CSS,Scriptをプリレンダリングされる。
・構築済みのHTMLをクライアントにresする。

具体例

・例えば、外部APIサーバーからデータを取得してリクエストに応じてサーバーでレンダリングする場合、下記の様になる。

Next.js document

//Next.js 
export defualt function Page ({data}){
//レンダーデータ
}
//これはリクエストごとに呼びされる関数、非同期に行われる。
export async function getServerSideProps() {
//外部APIからデータを取得する
const res = await fetch("https:// ~~ /data")
contt data = await res.json()
//propsを介してデータを渡す。
return { props : { data } };
}

詳細

・{ data } はこのコンポーネントに渡されるprops(プロパティ)です。このdataは外部からフェッチされたデータを含む。
・コンポーネントの中で、このdataを用いてUIをレンダリングする。
・getServerSidePropsはNext.jsの特別な関数で、ページがリクエストされるたびにサーバー側で実行される。
・非同期(async)は、外部APIからデータをフェッチするためにfetchを使用する。
・フェッチされたデータはJSON形式で解析され(await res.json())、data変数に保存される。
・最後に、dataをコンポーネントのpropsとして渡すために、{ props: { data } }をリターンする。
要は、
この関数によって、ページがレンダリングされる前に必要なデータを外部APIから取得し、ページコンポーネントに渡すことができる。

メリット

・初回のページの読み込みが早い
・SEO対策になる(Googleが完成されたHTMLを確認できるため)

デメリット

・2回目以降のページの表示が遅くなる。(リクエスト毎にサーバーで構築するため)
・サーバーの負荷量が大きくなる
・ページ間のナビゲーションが遅くなる可能性あり

採用が多い例

・ECサイト
・コンテンツ重視のサイト(ブログ、ニュースサイトなど)

SSG (静的サイト生成)

特徴

ビルド時にウェブページのHTMLが静的に生成

・サイトのビルド時に静的HTMLページが生成される。

静的ファイルの保存

・生成されたHTMLファイルはサーバー上に静的ファイルとして保存される。

クライアントからのリクエスト

・ユーザーがページをリクエストすると、対応する静的HTMLがレスポンスとして返される。

具体例

・SSGを採用する場合は2つのシナリオがある。
1.データを必要としないシンプルな静的なページの場合

function About (){
    return(
        <div>
        About
        </div>
);
}

export default About

2.データありの静的なページの場合
・以下は外部APIからデータをフェッチしてそれを使い静的なページをレンダリングする。
(ブログページがCMS(コンテンツ管理システム)からブログ投稿のリストをフェッチする必要がある場合)

export default function Blog ({posts}){

//ブログの投稿をレンダリングする
return(
 <ul>
    {posts.map((post) => (
    <li key={post.id}> {post.title} </li>
    ))}
</ul>
);
}

export async function getStaticProps(){
    //外部APIエンドポイントからブログの投稿のデータを取得する
    const res = await fetch("https:// .../posts")
    const posts = await re.json()

//{props:{posts}}を戻り値で返しBolgコンポーネントにデータを渡す
return {props:{posts}}
}

詳細

・getStaticProps関数はビルド時に呼び出され、外部APIからブログの投稿データを取得する。
・取得したデータはpropsとしてBlogコンポーネントに渡される。
・Blogコンポーネントはこのデータを利用してブログの投稿をリスト表示される。

メリット

・静的ファイルは高速に配信されるため、ページのロード時間が短縮される。
・サーバーにとって負荷が少なく、大量のトラフィックに対しても効率的。
・SEOに効果的になる

デメリット

・ビルド時に生成されるため、リアルタイムのデータ更新を反映するのが難しくなる。

採用される例

・ポートフォリオ
・ECサイトの商品ページ(特に更新がないから)
・ガイドやQ&Aなどの情報提供サイト

ISR (インクリメンタル静的再生成)

特徴

・コンテンツが時々変わるが、リアルタイムデータを表示する必要がないサイトに適している。

ビルド時に生成された静的ページを一定の間隔で自動的に再生成する。

定期的に内容を更新することができます。

最初はキャッシュされたページが表示されます。

具体例

以下はISRを使ったブログページの例です。

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}
 
// この関数はビルド時にサーバー側で呼び出される。
// 再検証が有効で新しいリクエストがある場合、
// サーバーレス関数で再び呼び出される可能性あり。
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  return {
    props: {
      posts,
    },
    // Next.jsは次の場合にページの再生成を試みる:
    // - 新しいリクエストが来たとき
    // - 最大でも10秒ごと
    revalidate: 10, // 秒単位
  }
}
 
// この関数もビルド時にサーバー側で呼び出される。
// パスがまだ生成されていない場合、サーバーレス関数で
// 再び呼び出される可能性があり。
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // プリレンダリングしたいパスを投稿に基づいて取得する
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // これらのパスのみをビルド時にプリレンダリングする。
  // { fallback: 'blocking' } は、パスが存在しない場合に
  // サーバーサイドでページをオンデマンドでレンダリングする。
  return { paths, fallback: 'blocking' }
}
 
export default Blog

・getStaticPropsはビルド時にサーバー側で実行され、外部APIからブログ投稿を取得して静的プロパティとして返す。
・revalidateプロパティにより、指定した秒数ごとにページが再生成される。
・getStaticPathsは動的なパスを持つページの事前レンダリングのために使用され、未生成のパスに対するリクエストがある場合には、それらのページがサーバーサイドでオンデマンドでレンダリングされる。

cache(キャッシュについて)

・最初のリクエスト後、10秒前までのページへのリクエストもすべてキャッシュされ、即座に処理されれる。
・10秒のウィンドウが経過した後も、次のリクエストではキャッシュされた (古い) ページが表示される。
・ページが正常に生成されると、キャッシュを無効にし、更新されたページを表示される。

メリット

・定期的な再生成により、コンテンツが常に最新の状態を保てる。
・全てのリクエストに対して動的なレンダリングを行う必要がなく、サーバーの負荷が軽減される。

デメリット

・revalidateの値を適切に設定する必要があり、サイトの内容に応じて調整が必要。
・ページを更新しないと前回のキャッシュのページが表示される。

採用される例

定期的な更新が必要だがリアルタイムのデータを表示する必要はないウェブサイト。

・ニュースサイトやブログ:
定期的に新しい記事や投稿が追加されるが、リアルタイムの更新が不要な場合。

・製品カタログやECサイト:
新製品の追加や既存製品の情報更新があるが、リアルタイムの在庫情報表示が必要ない場合。
ISRにより、新しい製品情報や更新された製品説明が定期的にページに反映される。
・教育機関や企業のウェブサイト:
新しいコース、プログラム、企業ニュースなどの情報が定期的に更新される。
ISRを使用して、新しい情報を効果的にウェブサイトに追加できる。

まとめ

・今回はNext.jsのレンダリングについて備忘録としてまとめた。
・プロジェクトごとでどれを採用するか決めるのではなく、ページごとに最適なレンダリングを選択することが大事。

Discussion