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 之前,頁面也是不可操作,也就是我架構篇章提到的僵直時間。