注意: 我們正在改進 Next.js 的路由。 Layouts RFC 提供更多詳細的資訊及回饋管道。
在 Next.js 中,一個頁面( Pages ) 就是從 pages
目錄底下匯出的一個 React 元件,它可以是 .js
、 .jsx
、 .ts
、 .tsx
任意一種檔案格式。每個檔案的檔名會進而關聯到一個頁面的路由。
範例:如果你建立了 pages/about.js
檔案,並匯出了如下的 React 元件,你將會在網站的 /about
頁面下看到它。
function About() { return <div>About</div> } export default About
Next.js 支援具有動態路由的頁面。例如,如果你建立了一個名為 pages/posts/[id].js
的檔案,那麼你將可以在網站的 posts/1
、posts/2
等路徑下看到它。
想要學習更多有關動態路由的資訊,請參閱 動態路由文件。
在預設的情況下,Next.js 會提前渲染( pre-render ) 每個頁面。這意味著 Next.js 會提前為每個頁面產生 HTML,而不是讓所有事情都由客戶端 JavaScript 完成,這可以為我們帶來更好的效能與搜尋引擎最佳化( SEO )。
每個產生的 HTML 都會與該頁面所需要的最少 JavaScript 程式碼相關聯。當瀏覽器載入一個頁面時,它的 JavaScript 程式碼會執行並使頁面可以讓使用者進行互動(這個過程被稱為 hydration )。
Next.js 有兩種形式的提前渲染:靜態生成( Static Generation )和伺服器端渲染( Server-side Rendering )。兩者的差別在於它們產生頁面 HTML 的時機。
更重要的是,Next.js 讓你可以選擇在每個頁面中使用哪種形式的提前渲染。你可以建立一個「混合」的 Next.js 應用程式,並使用靜態生成來處理大部分的頁面,並使用伺服器端渲染來處理其他頁面。
我們建議在效能方面使用靜態生成而不是伺服器端渲染。靜態生成的頁面可以透過 CDN 進行快取,而不需要額外的設定來提升效能。然而,在某些情況下,伺服器端渲染可能是唯一的選擇。
你也可以在靜態生成或伺服器端渲染的頁面中使用客戶端資料取得。這意味著頁面的某些部分可以完全由客戶端 JavaScript 進行渲染。要學習更多相關內容,請參閱資料獲取文件。
如果一個頁面使用靜態生成,頁面 HTML 將在建置時產生。這意味著在生產環境中,當你執行 next build
時,頁面 HTML 將會被產生。然後,這個 HTML 將會在每個請求中被重複使用,也因此它可以被 CDN 快取。
在 Next.js 中,你可以使用或不使用資料來靜態生成頁面。讓我們來看看每個情況。
在預設的情況下,Next.js 使用靜態生成預先渲染頁面,而不會抓取資料。這是一個範例:
function About() { return <div>About</div> } export default About
注意到這個頁面不需要抓取任何外部資料來預先渲染。在這種情況下,Next.js 在建置時為每個頁面產生一個 HTML 檔案。
有些頁面需要抓取外部資料來預先渲染。這個時候會分成兩種情況,也可能同時屬於兩個情況。在每種情況下,你可以使用 Next.js 提供的這些函數:
getStaticProps
。getStaticPaths
(通常會和 getStaticProps
一起使用)。範例:你的部落格頁面可能需要從 CMS (內容管理系統)抓取部落格文章列表。
// TODO: Need to fetch `posts` (by calling some API endpoint) // before this page can be pre-rendered. function Blog({ posts }) { return ( <ul> {posts.map((post) => ( <li>{post.title}</li> ))} </ul> ) } export default Blog
為了在預先渲染時抓取這些資料,Next.js 允許你從同一個檔案 export
一個 async
函數,這個函數叫做 getStaticProps
。這個函數會在建置時被呼叫,並讓你在預先渲染時傳遞抓取到的資料給頁面的 props
。
function Blog({ posts }) { // Render posts... } // This function gets called at build time export async function getStaticProps() { // Call an external API endpoint to get posts const res = await fetch('https://.../posts') const posts = await res.json() // By returning { props: { posts } }, the Blog component // will receive `posts` as a prop at build time return { props: { posts, }, } } export default Blog
要學習更多關於 getStaticProps
如何運作的資訊,請參考 資料抓取文件。
Next.js 允許你建立具有動態路徑的頁面。例如,你可以建立一個名為 pages/posts/[id].js
的檔案,來顯示一篇部落格文章,這篇文章的 id
是動態的。這樣就可以讓你在存取 posts/1
時,顯示 id: 1
的部落格文章。
要學習更多關於動態路由的資訊,請參考 動態路由文件。
然而,你在建置時想要預先渲染哪個 id
可能會取決於外部資料。
範例:假設你只在資料庫中新增了一篇部落格文章(並且他的 id: 1
)。在這種情況下,你只想在建置時預先渲染 posts/1
。
之後,你可能會新增第二篇部落格文章(並且他的 id: 2
)。然後你也想要預先渲染 posts/2
。
所以你的頁面路徑會依賴外部資料。為了處理這個情況,Next.js 允許你從一個動態頁面(在這個範例中,指的是 pages/posts/[id].js
) export
一個 async
函數,叫做 getStaticPaths
。這個函數會在建置時被呼叫,並讓你指定哪些路徑你想要預先渲染。
// This function gets called at build time export async function getStaticPaths() { // Call an external API endpoint to get posts const res = await fetch('https://.../posts') const posts = await res.json() // Get the paths we want to pre-render based on posts const paths = posts.map((post) => ({ params: { id: post.id }, })) // We'll pre-render only these paths at build time. // { fallback: false } means other routes should 404. return { paths, fallback: false } }
同時,在 pages/posts/[id].js
中,你也需要 export
一個 getStaticProps
函數,這樣你就可以抓取這個 id
的部落格文章資料,並用它來預先渲染頁面:
function Post({ post }) { // Render post... } export async function getStaticPaths() { // ... } // This also gets called at build time export async function getStaticProps({ params }) { // params contains the post `id`. // If the route is like /posts/1, then params.id is 1 const res = await fetch(`https://.../posts/${params.id}`) const post = await res.json() // Pass post data to the page via props return { props: { post } } } export default Post
想要學習更多關於 getStaticPaths
如何運作的資訊,請參考 資料抓取文件。
我們建議你盡可能使用靜態生成(無論頁面是否需要外部資料),因為你的頁面可以被建置一次,並且由 CDN 提供服務,這使得它比在每個請求中由伺服器渲染頁面來的快。
你可以使用靜態生成來建立許多種類的頁面,包括:
你要問自己一個問題:「我可以在使用者請求之前預先渲染這個頁面嗎?」如果答案是肯定的,那麼你應該選擇靜態生成。
在另一個方面,如果你不能在使用者請求之前預先渲染頁面,那麼靜態生成就不是一個好主意。也許你的頁面會顯示頻繁更新的資料,並且每個請求都會改變頁面內容。
在這種情況下,你可以做以下其中一件事:
也被稱為「SSR」或「動態渲染」。
如果一個頁面使用伺服器端渲染,那麼頁面的 HTML 會在每個請求中被生成。
為了使用伺服器端渲染,你需要 export
一個 async
函數,並且命名為 getServerSideProps
。這個函數會在每個請求中被伺服器呼叫。
舉例來說,假設你的頁面需要預先渲染頻繁更新的資料(從外部 API 抓取的資料)。你可以寫一個 getServerSideProps
函數,它會抓取這些資料,然後像下面這樣把它們傳遞給 Page
:
function Page({ data }) { // Render data... } // This gets called on every request export async function getServerSideProps() { // Fetch data from external API const res = await fetch(`https://.../data`) const data = await res.json() // Pass data to the page via props return { props: { data } } } export default Page
如同上面你所看到的,getServerSideProps
與 getStaticProps
很相似,但是它們的差別在於 getServerSideProps
會在每個請求中被呼叫,而不是在建置時。
要了解更多關於 getServerSideProps
如何運作的資訊,請參考我們的資料抓取文件。
我們在這篇文章討論了 Next.js 的兩種預先渲染方式。
export
頁面元件,或者 export
getStaticProps
(如果需要的話也可以 export
getStaticPaths
)。這對於可以在使用者請求之前預先渲染的頁面來說是很棒的。你也可以和客戶端渲染一起使用,以便帶入額外的資料。export
getServerSideProps
。 因為伺服器端渲染比靜態生成的效能較差,所以只有在絕對必要的情況下才使用。我們建議你接著閱讀下面的章節: