當初在面試時,被問到 Next.js 的 Server Side Render,想在這裡整理一下:
先介紹一下什麼是 Server Side Render:
在傳統的 React SPA(Single Page Application)中,使用者請求後,瀏覽器會先下載一個空的 index.html,再由 JS 在客戶端「渲染出整個畫面」。但這樣搜尋引擎的爬蟲可能只看到一個空頁,導致 SEO 不佳;使用者首次請求頁面時,也需要等待 JS bundle 下載和執行,導致使用者體驗不佳。
而 Server Side Render,則會在使用者請求後,先由 Next.js 伺服器將 React 的元件渲染成 html,JS 再在前端進行「hydration(注水、水合)」,讓靜態的 HTML 變成有互動的應用,同時能避免上述 SPA 的問題。
在 Next.js 12 以前,採用的是 Page Router。它的 SSR 實作方式主要是透過 getServerSideProps 函式來實現。什麼是 getServerSideProps 函式?它是 Next.js 提供的預先取資料的函式,流程如下:
來看個範例:
// pages/profile.js
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/user')
const user = await res.json()
return {
props: { user }, // 傳給下方的 component
}
}
export default function ProfilePage({ user }) {
return <div>Hello, {user.name}</div>
}
當使用者請求 /profile,Next.js 伺服器先執行 getServerSideProps(),取得資料 (fetch),再渲染 html Hello, Alice,html 再將交給瀏覽器,前端再 hydration。
getServerSideProps 實現了 SSR,但它也有一些缺點與限制:
在 Next.js 13 推出的 App Router 架構中,官方徹底重構了 SSR 的運作方式,不再使用 getServerSideProps 來觸發伺服器端渲染,轉而使用了 React Server Components (RSC) 架構。在 App Router 中,預設每個檔案都是 Server Component,他的執行流程有什麼不同呢?直接來看範例:
// app/profile/page.tsx
export default async function ProfilePage() {
const res = await fetch("https://api.example.com/user", { cache: "no-store" });
const data = await res.json();
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
當使用者請求 /profile,Next.js 伺服器判斷這是要渲染 App Router 的頁面後,就會進入 React Server Components (RSC) 的執行階段,在伺服器中執行 fetch ,並組成 React Server Components Tree(RSC tree),不同於過去 Page Router 時代直接回傳完整的 html,Next.js 會先產生一份 React Server Components Payload (RSC Payload),同時再生成對應的 html 給首次請求的使用者。瀏覽器接收到 html 後,而前端 JS 隨後進行 hydration,將靜態的 html 轉換為可互動的 React 元件。
這邊再補充一下:
總結來說,Next.js 的 SSR 由 Page Router 到 App Router 的演進,其核心是 將伺服器端渲染更自然地融入 React 組件架構。