# 翻譯筆記 - Next.js: 網頁渲染的終極 Cheat Sheet

Next.js 有四種渲染策略

- Static-Site Generation (SSG)
- Server-Side Rendering (SSR)
- Incremental Static Regeneration (ISR)
- Client-Side Rendering (CSR)

此外，這 Cheat Sheet 清楚地展示每種策略會如何影響 Core Web Vitals 指標：TTFB、FP、FCP、LCP、TBT 和 TTI。

![Next.js: The Ultimate Cheat Sheet to Page Rendering](https://cdn.hashnode.com/res/hashnode/image/upload/v1630583530455/rSFSg5rOX.jpeg)

從 Cheat Sheet 的左側可以發現從 Build 到渲染給 Client 端，會經過六個步驟。右側的話可以知道不同步驟在 Server 或 Client 的處理流程中發生的位置。每個策略列可以看到不同的步驟中的執行內容。還能知道每個步驟中看到稱成的內容（HTML - React/CSS/JavaScript/Data）。

在此篇文章中，作者有提供一個  [Live Demo](https://next-page-rendering.vercel.app/)  的網頁。(但有些我看不太出來差異，要搭配開發者工具去比較比較清楚)

# Static-Site Generation (SSG)
此策略是 Next.js 的預設策略。Next.js 推薦使用此策略達到最好的效能，產生可讓 CDN 使用的靜態內容，而達到更快的  [Time to First Byte(TTFB)](https://web.dev/time-to-first-byte/) 

### Step 1
Next.js 在 HTML 中生成一個 Single Page App 以及伺服器上的 `.css` 和 `.js`。此步驟稱為 pre-rendering，只會在下一個 build 指令執行時，執行一次。在此步驟中，所有網頁的程式(HTML) 都會生成，包括 React、CSS、JavaScript 以及所需的資料（如果網頁使用任何資料取得方法，例如  [`getStaticProps()`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)  或  [`getStaticPaths()`](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation) ）

### Step 2
Client 從 CDN 請求 Single Page App

### Step 3
Client 從 CDN 下載 Single Page App

### Step 4
Client 解析並渲染 Single Page App 到瀏覽器中。 這是觸發三個 Core Web Vitals 指標的地方（First Paint、First Contentful Paint 和 Largest Contentful Paint）。因為網頁的 HTML 已經在伺服器渲染，所以 Client 瀏覽器只需要按原樣載入和渲染。而且由於網頁的編碼方式允許瀏覽器使用最少的 JavaScript 呈現，因此可以將 render-blocking 的影響減少到最低限度，從而得到很好的效能。

### Step 5
React(JavaScript)程式碼會以 (re)Hydrate 網頁。即使網頁已經 pre-built，也需要這個額外的步驟來讓 React 將 JavaScript 添加或啟用到 Virtual DOM 使其網頁有互動性。因為此時執行 JavaScript，所以所有的渲染阻塞時間都受此額外步驟影響。此外，由於瀏覽器必須等待 hydration 過程完成，因此互動時間也會受到影響

### TypeScript 範例

```
// Next.js libraries
import Head from 'next/head';

// Custom Components
import BackToHome from 'components/BackToHome';

// Page component
export default function StaticSideGeneration({ jsonData }) {
    return (
        <>
            <Head>
                <title>Static-Site Generation (SSG) • Guy Dumais</title>
                <meta
                    name='description'
                    content='Example page using Static-Site Generation (SSG) with Next.js 11 and React 17'
                />
                <meta
                    name='viewport'
                    content='initial-scale=1.0, width=device-width'
                />
            </Head>
            <BackToHome />
            <h1>Static-Site Generation (SSG)</h1>
            <p>
                Data fetched at build-time on the server-side before sending to
                the client.
            </p>
            <ul>
                {jsonData.data.map((e) => (
                    <li key={e.id}>{e.email}</li>
                ))}
            </ul>
        </>
    );
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
    const res = await fetch('https://reqres.in/api/users?page=2');
    const jsonData = await res.json();

    return {
        props: {
            jsonData, // will be passed to the page component as props
        },
    };
}
``` 

# Server-Side Rendering (SSR)
Server-Side Rendering 是 Next.js 提供的第二種產生網頁的策略。Next.js 建議盡可能地避免使用此策略，因為靜態內容只針對每個請求 built（pre-rendered）和 distributed。所以建制過程需要額外的時間，Time to First Byte (TTFB) 指標會增加從而導致不好的結果。

### Step 1
在此步驟 Next.js 不會產生(pre-render)任何網頁

### Step 2
Client 向伺服器請求 Single Page App，Next.js 在 HTML 檔案中產生(pre-render)一個 Single Page App 和其所需的 `.css` 和 `.js`。在此步驟中，所有的網頁程式碼(HTML)都會產生，包括 React、CSS、JavaScript 以及所需的資料（如果網頁使用 [`getServerSideProps()`](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) 來獲取資料）

### Step 3
Client 從伺服器下載 Single Page App

### Step 4
和 SSG 第四步驟一樣

### Step 5
和 SSG 第五步驟一樣

### TypeScript 範例

```
// Next.js libraries
import Head from 'next/head';

// Custom Components
import BackToHome from 'components/BackToHome';

// Page component
export default function ServerSideRendering({ jsonData }) {
    return (
        <>
            <Head>
                <title>Server-Side Rendering (SSR) • Guy Dumais</title>
                <meta
                    name='description'
                    content='Example page using Server-Side Rendering (SSR) with Next.js 11 and React 17'
                />
                <meta
                    name='viewport'
                    content='initial-scale=1.0, width=device-width'
                />
            </Head>
            <BackToHome />
            <h1>Server-Side Rendering (SSR)</h1>
            <p>
                Data fetched on the server-side at <b>each</b> request before
                sending to the client.
            </p>
            <ul>
                {jsonData.data.map((e) => (
                    <li key={e.id}>{e.email}</li>
                ))}
            </ul>
        </>
    );
}

export async function getServerSideProps() {
    const res = await fetch('https://reqres.in/api/users?page=2');
    const jsonData = await res.json();

    return {
        props: {
            jsonData, // will be passed to the page component as props
        },
    };
}

```

# Incremental Static Regeneration (ISR)
Next.js 的第三個策略，此策略除了可以在網頁更新時 rebuilt 之外，其餘的都跟 Static Site Generation 一樣。

### Step 1
與 SSG 第一步驟一樣

### Step 2
Client 從 CDN 請求 Single Page App。此外，如果網頁使用取得資料方法 `getStaticProps()` 來和 `revalidate` 選項結合使用。如果取得回傳的資料並更新，則會重新產生網頁。Next.js 建議使用此方法在超過 1,000 個網頁的大型網站。因爲 pre-render 每個網頁都需要時間，所以使用此方法只會在第一次請求或是網頁內容更新時 pre-render。

### Step 3
與 SSG 第三步驟一樣

### Step 4
與 SSG 第四步驟一樣

### Step 5
與 SSG 第五步驟一樣

## TypeScript 範例

```
// Next.js libraries
import Head from 'next/head';

// Custom Components
import BackToHome from 'components/BackToHome';

// Page component
export default function IncrementalStaticGeneration({ jsonData }) {
    return (
        <>
            <Head>
                <title>
                    Incremental Static Regeneration (ISR) • Guy Dumais
                </title>
                <meta
                    name='description'
                    content='Example page using Incremental Static Regeneration (ISR) with Next.js 11 and React 17'
                />
                <meta
                    name='viewport'
                    content='initial-scale=1.0, width=device-width'
                />
            </Head>
            <BackToHome />
            <h1>Incremental Static Regeneration (ISR)</h1>
            <p>
                Data fetched at build-time on the server-side and rebuilt when
                data updated.
            </p>
            <ul>
                {jsonData.data.map((e) => (
                    <li key={e.id}>{e.email}</li>
                ))}
            </ul>
        </>
    );
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
    const res = await fetch('https://reqres.in/api/users?page=2');
    const jsonData = await res.json();

    return {
        props: {
            jsonData, // will be passed to the page component as props
        },

        // Next.js will attempt to re-generate the page:
        // - When a request comes in
        // - At most once every second
        revalidate: 100, // In seconds
    };
}
```

# Client-Side Rendering (CSR)
Next.js 的第四個策略，除了部分內容可以在 Client 建立之外，其餘的都跟 Static Site Generation 一樣。

### Step 1
與 SSG 第一步驟一樣，不同的地方在於內容(資料) 既不 pre-render 也不包含在 static bunch。這將產生更小的檔案大小，因此可能縮短下載時間。

### Step 2
與 SSG 第二步驟一樣，但是不包含內容(資料)

### Step 3
與 SSG 第三步驟一樣，但是不包含內容(資料)

### Step 4
與 SSG 第四步驟一樣

### Step 5
與 SSG 第五步驟一樣，但是不包含內容(資料)

### Step 6
Client 獲得內容(data)並且 React 更新 UI。此方法很有用，例如，當想要顯示具有靜態內容的骨架網頁，然後將資料逐步注入到網頁，需要長時間等待資料回傳。

## TypeScript 範例

```
// React
import { useEffect, useState } from 'react'

// Next.js
import Head from 'next/head'
import Link from 'next/link'

// Custom Components
import BackToHome from 'components/BackToHome'

// Page component
export default function ClientSideRendered() {

    const [state, setState] = useState([] as any)

    const getData = async () => {
        const res = await fetch('https://reqres.in/api/users?page=2')
        const jsonData = await res.json()
        setState(jsonData)
    }

    useEffect(() => {
        getData()
    }, [])

    return (
        <>
            <Head>
                <title>Client-Side Rendering (CSR) • Guy Dumais</title>
                <meta name="description" content="Example page using Client-Side Rendering (CSR) with Next.js 11 and React 17" />
                <meta name="viewport" content="initial-scale=1.0, width=device-width" />
            </Head>
            <BackToHome />
            <h1>Client-Side Rendering (CSR)</h1>
            <p>Data fetched on the client-side only.</p>
            <ul>
                {
                    state.data?.map((e) => (
                        <li key={e.id}>{e.email}</li>
                    ))
                }
            </ul>
        </>
    )
}
```
