翻譯筆記 - Next.js: 網頁渲染的終極 Cheat Sheet
翻譯自 https://medium.com/@gdumais/next-js-the-ultimate-cheat-sheet-to-page-rendering-46e4f82ce1ee
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。
從 Cheat Sheet 的左側可以發現從 Build 到渲染給 Client 端,會經過六個步驟。右側的話可以知道不同步驟在 Server 或 Client 的處理流程中發生的位置。每個策略列可以看到不同的步驟中的執行內容。還能知道每個步驟中看到稱成的內容(HTML - React/CSS/JavaScript/Data)。
在此篇文章中,作者有提供一個 Live Demo 的網頁。(但有些我看不太出來差異,要搭配開發者工具去比較比較清楚)
Static-Site Generation (SSG)
此策略是 Next.js 的預設策略。Next.js 推薦使用此策略達到最好的效能,產生可讓 CDN 使用的靜態內容,而達到更快的 Time to First Byte(TTFB)
Step 1
Next.js 在 HTML 中生成一個 Single Page App 以及伺服器上的 .css
和 .js
。此步驟稱為 pre-rendering,只會在下一個 build 指令執行時,執行一次。在此步驟中,所有網頁的程式(HTML) 都會生成,包括 React、CSS、JavaScript 以及所需的資料(如果網頁使用任何資料取得方法,例如 getStaticProps()
或 getStaticPaths()
)
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()
來獲取資料)
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>
</>
)
}