Next 的 SSR(Server-Side Rendering)的概念是在每個使用者請求時動態產生頁面內容,而不是提前產生靜態 HTML 頁面,這樣頁面可以根據使用者的需求和上下文動態渲染,適用於需要即時資料或使用者特定資訊的場景。
想解決的問題:
適用場合:
以下是一個範例來解釋 SSR 模式的精神以及如何使用 Next.js 實作:
// pages/location/index.tsx
import React from 'react'
interface RickandmortyLocation {
id: number;
name: string;
type: string;
dimension: string;
residents: string[];
url: string;
created: string;
}
interface RickandmortyLocationRes {
info: {
count: number,
pages: number,
next: string | null,
prev: string | null,
},
results: RickandmortyLocation[]
}
const Location = ({ apiData }:{ apiData: RickandmortyLocationRes}) => {
return (
<div>
<h2>Location Page</h2>
<div>
{apiData?.results?.map((locate) => (
<div key={locate.id}>
<p>{locate.name}</p>
</div>
))}
</div>
</div>
)
}
export async function getServerSideProps() {
try{
const res = await fetch("https://rickandmortyapi.com/api/location");
if (!res.ok) {
return { notFound: true };
}
const apiData: RickandmortyLocationRes = await res.json();
return {
props: {
apiData,
},
};
} catch(err) {
console.log('err',err);
}
}
export default Location
getServerSideProps
只會在伺服器端運行並不會在瀏覽器執行,如果這個頁面使用 getServerSideProps
,當你直接請求這個頁面時,getServerSideProps
在請求的時候運行,這個頁面會和回傳的 props 一起提前渲染。
在不透過Next 的情況下要怎麼實現SSR? 假設一個簡單的counter component如下:
// shared/components/Counter.jsx
import { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const onAdd = () => setCount(count+1);
return (
<>
<p>{count}</p>
<button onClick={onAdd}>++</button>
</>
);
};
透過 server 渲染只能拿到靜態的html
// server/index.js
import ReactDOMServer from "react-dom/server";
import express from "express";
require("node-jsx").install();
const app = express();
app.get("/", (req, res) => {
const reactHtml = ReactDOMServer.renderToString(<Counter />)
const htmlTemplate = `
<!DOCTYPE html>
<html>
<head>
<title>Universal React server bundle</title>
</head>
<body>
<div id="root">${reactHtml}</div>
<script src="public/client.bundle.js"></script>
</body>
</html>
`;
res.send(htmlTemplate)
});
這個時候拿到的 button
是不能動的,老時代的 SSR 作法就到此為止了,但 React 的作法是需要用戶端根據 server 產生的頁面,繼續二次渲染、事件綁定等。
// client/index.jsx
import React from 'react';
import { hydrate } from 'react-dom';
hydrate(<Counter />, document.getElementById('root'));
renderToString
直接渲染出的頁面信息為靜態html。因此,若要使用react 來實現服務端渲染,一般需要3個目錄,工程配置比較繁瑣。
而使用Next的話,透過 getServerSideProps
就能輕鬆完成上述的工作。
getServerSideProps
請求完成才可以返回 html,雖不是空白頁面,但完成 hydrate 之前,頁面也是不可操作,也就是我架構篇章提到的僵直時間。