Closed6
フロントエンドのレンダリングについて(Nuxt.js/Next.js)
フロントエンドの歴史
フロントエンドの実装方式
静的サイト
- HTML/CSS/JavaScriptのみで作成されたページ
CSR(Client Side Rendering)
- アクセスしてきたユーザのブラウザ上(Client)でHTMLを動的に生成する方式。
SPA (Single Page Application)
- CSRを使うことで、1つのページのみでサイト(Application)を表現する方式
- サーバから受け取ったデータによって1つのページのDOM要素を更新していくことで、1つのページのみで サイトを表現することができる
SSR(Server Side Rendering)
- サーバ上で動的にHTMLを生成し、アクセスしてきたユーザのブラウザに返す方式。
- SSRより表示速度は速いが、サーバ負荷がかかる。
SSG (Static Site Generation)
- 開発時に全てのページを静的なHTMLとして生成しておく方法
- 静的サイトなので速度は速いが、ビルドするのに時間がかかる。
- 静的サイトなので、動的にHTMLの変更をすることができない。
ISR(Incremental Static Regeneration)
- 基本はSSGで生成された静的ページ
- 有効期限を超えたら非同期で静的ページの再生成をSSRで行う。
フロントエンドのトレンド
Nuxt.js
- ハイブリットレンダリングでページごとにレンダリングモードを指定できる。
- ユニバーサルレンダリングは初回はSSR、ページ描画後の動的な処理についてはCSRで行う?
- ISRについてはNetlify または Vercelのプラットフォームでのみサポートされているらしい。
ブラウザーがユニバーサル (サーバー側 + クライアント側) レンダリングを有効にして URL を要求すると、サーバーは完全にレンダリングされた HTML ページをブラウザーに返します。
動的なインターフェイスやページ遷移など、クライアント側のレンダリング方法の利点を失わないように、HTML ドキュメントがダウンロードされると、クライアント (ブラウザー) はバックグラウンドでサーバー上で実行される JavaScript コードを読み込みます。ブラウザーはそれを再度解釈し (したがってユニバーサル レンダリング)、Vue.js がドキュメントを制御して対話性を可能にします。
ハイブリットレンダリングの設定
-
routeRules
で個別のページのレンダリングモードを設定する -
prerender: true
にするとSSGができるようになる。
nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
"/csr": { ssr: false },
"/ssr": { ssr: true },
"/ssg": { ssr: true, prerender: true },
},
build: {
transpile: ["vuetify"],
},
modules: ["@nuxtjs/tailwindcss"],
css: ["~/assets/css/global.css"],
});
CSR
-
nuxt.config.ts
のrouteRules
でssr : false
にする。 - 通信で送られてくるhtmlドキュメントは空
- クライアント側でDOM要素が動的に生成されている。
<template>
<div>
<h1>Client Side Rendering Example</h1>
<div>
<p>Id: {{ data?.id }}</p>
<p>Title: {{ data?.title }}</p>
<p>UserId: {{ data?.userId }}</p>
</div>
</div>
</template>
<script setup lang="ts">
const { data } = (await useAsyncData("run_useAsyncData", () => {
return $fetch("https://jsonplaceholder.typicode.com/todos/1");
})) as { data: Ref<Record<string, string>> };
</script>
ユニバーサルレンダリング
- 全てのページの要素があらかじめ、サーバから渡されている
- クライアント側でのイベントをフックして動的に生成したdom要素は含まれていない。
-
onMounted
で動的に生成されたdom要素は含まれない。
-
<template>
<div>
<h1>Client Side Rendering Example</h1>
<div>
<p>Id: {{ data?.id }}</p>
<p>Title: {{ data?.title }}</p>
<p>UserId: {{ data?.userId }}</p>
<p>{{ csr }}</p>
</div>
</div>
</template>
<script setup lang="ts">
const { data } = (await useAsyncData("run_useAsyncData", () => {
return $fetch("https://jsonplaceholder.typicode.com/todos/1");
})) as { data: Ref<Record<string, string>> };
const csr = ref("");
onMounted(() => {
csr.value = "client rendering data";
});
</script>
SSG
-
nuxt.config.ts
のrouteRules
でprerender : true
にする。 - ビルド時にページが事前生成される。
コード例
Next.js
- app-routerとpage-routerでレンダリングの実装の仕方がだいぶ異なる
- 今回はapp-routerで試している。
- app-routerでは基本的にSSRで動き、CSRをしたいときは明示的に
use client
と宣言を入れる必要がある。
CSR
-
use client
宣言がされているかつ、useState
の使用がされている場合にCSRになる。 - Nuxt.jsのCSRと違い、クライアントで動的に生成されるところ以外のhtmlドキュメントはサーバから返ってくる。(通信で送られてくるhtmlが空じゃない)
"use client";
import { useState, useEffect } from "react";
interface Todo {
userId: number;
id: number;
title: string;
}
const CSRendering = () => {
const [data, setData] = useState<Todo | null>(null);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const data: Todo = await response.json();
setData(data);
};
return (
<div>
<h1>Client Side Rendering Example</h1>
<div>
<p>Id: {data?.id}</p>
<p>Title: {data?.title}</p>
<p>UserId: {data?.userId}</p>
</div>
</div>
);
};
export default CSRendering;
SSR/SSG/ISR
-
fetch
メソッドでno-store
を設定すると、SSRになる。 -
force-cache
にするとSSGになる。 - revalidateを設定するとISRになる。
interface SsrTodo {
userId: number;
id: number;
title: string;
completed: boolean;
}
export default async function SsrPage() {
const todo: SsrTodo = await getData();
return (
<ul>
<li> Id : {todo.id}</li>
<li> userId : {todo.userId}</li>
<li> title : {todo.title}</li>
<li> completed : {todo.completed.toString()}</li>
</ul>
);
}
async function getData() {
// SSR
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
cache: "no-store",
});
// SSG
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
cache: "force-cache",
});
return res.json();
}
// ISR
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
next: { revalidate: 60 },
});
コード例
感想
- Next.jsとNuxt.jsを比べてみると、レンダリングだけでも違うことが多い
- 個人的にはNext.jsの方が一歩リードしている印象を受けた
このスクラップは2024/03/24にクローズされました