快速入門 Next.Js 13 App Router, RSC(React Server Component), SEO相關說明

前言

Next.Js 13 以後,推出 App Router 以及 RSC(React Server Component) 功能
簡單說就是在 Server 將 React Component 準備好後,再傳到 Client 去渲染出來

可以帶來下列優勢

  1. 避免下載只在 Server 使用的套件,降低 JS 的 Bundle Size,提高頁面載入速度
  2. Server Component 可以完整使用後端資源,如 fs、查詢 DB
  3. 避免 waterfall 請求。如多層 useEffect 呼叫 API 取得資料。有時可以降低組件寫法的複雜度,變得直白單純
  4. 官方建議盡量將 API 寫在 Server,以盡早取得資料。有互動需求在額外打 API 時再寫在 Client

當然也會有額外缺點

  1. Server Component 無法使用 hook。如熟悉的 useState, useEffect
  2. Server Component 無法操作 dom,也沒有 localStorage 等 browser 才有的東西
  3. 提高學習曲線,就算是純前端,也需要了解 Server 在做什麼,不在只是無腦打 API
  4. 需要額外拆解組件的邊界 ── 哪些適合放在 Server、哪些適合放在 Client

Next.Js 13 以後,將 RSC 列為正式功能,也多了許多功能僅能在 App Router 使用
看得出來之後應會逐漸側重 RSC 與 App Router。並也呼籲 Next 生態圈可以更新一波,標明清楚自己的套件該使用於何處

單純整理了一些自己實務開發上遇到的狀況與一些常見的情境,難免疏漏
更多細節仍建議至官方教學查詢

雖然內容有點多,還是推薦花些時間過一遍,可以少踩一些坑 XD

筆記

Page Router 大家應該都算熟悉了,就不另做介紹,僅整理 App Router 與 RSC(React Server Component)

next 13.4.5以後,不必另外在next.config.js增加下列參數啟用 App Router。預設已直接啟用

experimental: {
    appDir: true
  },

App Router vs Page Router

  • App Router 在 app 目錄底下;Page Router 在 page底下
  • 都是由目錄結構產生實際路由,但兩邊路由不可重複。即app/profile/page.tsxpage/profile.tsx會產生相同路徑,執行時會噴錯提醒
  • Page Router 熟悉的_document.tsx, _app.tsx,App Router 已不使用。取而代之的是app/layout.tsx。所以 RootLayout 裡需要自行寫<html><body>這些,因為 Next 不會自動產生
  • 後端 API 路徑結構一樣,都是/api/底下。即page/api/user.tsx等於app/api/user/route.tsx,都會產生/api/user。與路由一樣,相同 api 在 page 與 app 不可重複
  • 蒐集網址參數(動態路由)命名一樣是[param][...params],差別在 App Router 僅能是目錄,而 Page Router 可以是檔名
  • 在組件裡取得路由(Dynamic route)參數,Page Router 是用useRouter(); App Router 是直接在 props 傳入,如 page({params})

App Router

  • React Component 須使用'use client', 'use server'標記組件類型
  • app/目錄底下不標記的話,預設皆是 RSC(React Server Component)
  • 只要組件有傳遞到onClick之類的與 user 互動的 function,皆須為 client 組件。官方推薦可以單純將其包成一小組件並宣告'use client'
  • Server 與 Client 組件可以交錯建立成一棵樹,唯須注意 props 不可為 Date, function 等無法被json 序列化的函式
  • 宣告'use client'後,其底下引入使用的組件不必特別宣告,自動當作client。直到再次遇到'use server',而server底下引入的組件亦同。自動視為server組件直到遇到'use client'。故不必在每個組件最上面聲明 clientserver。沒聲明的組件,自己要清楚使用位置
  • RSC 無法使用 hook、dom API,但可以在組件裡做 Server 才能做的事,如 fs, 查詢 DB…等。若有 hook 需求,請轉成 client component
  • 所有的頁面節點一律由固定檔名page.tsx(js)才會被渲染成路由,故相同功能組件可置於app/目錄底下,集中管理。不必放在外面讓檔案距離遙遠,更易於管理
  • 所有API節點一律由route.ts(js)才有其api。檔案管理優點同上。裡面則額外export function GET, export const POST等指定 API method
  • page 頁面沒有getServerSideProps, getStaticPaths 等函式,只有 export react component
  • 不同連結要使用相同layout,可建立/(group)/目錄來分組,其中(group)不會被渲染成路徑。如app/(group)/abc/edf ->/abc/edf
  • 額外提供headerscookies可以在 RSC 或 API 使用。具體用法可參考官方文件要注意的是,在 Page Router 無法使用。若在 Page Router 使用,會輸出undefined
  • 在 RSC 使用 fetch Call API時,他的 fetch 是繼承 native fetch 後再增加一些 next 專屬功能,如 cache ,亦有專屬的 next: {...} 參數可使用
  • 以下檔名是 next 專屬檔名,會被 next 處理。不能做為自己的子組件命名
    • page
    • layout
    • route
    • loading
    • not-found
    • error
    • global-error
    • template
    • default
  • 會有全域 provider (如redux, react query)的三方套件。官方推薦針對他增加一個provider.tsx,在最上層使用'use client',再放到layout.tsx裡。具體範例可參考官方 / Rendering third-party context providers in Server Components
  • 若有些函式可能包含機密資訊,但寫法又可以在 Client 運作,想要限制他僅能在 Server 的話,可使用server-only套件保護。用法可見官方 / The “server only” package

‘use client’ 與 SEO 問題

(2023/09/08更新)

看到'use client',會認為僅在 Client render
若考量 SEO,很直覺的就會認為應該盡可能的放在 Server,client 越少越好,甚至少到只有互動部份
結果就會發現以前熟悉的 React 變得很難寫,因為放在 Server 後,就不能用 hook,導致很多以前簡單的事情變得很困難

但其實'use client'的行為是像以前的 SSR (getServerSideProps),他是在 Server render 完後,再到 Client 去 hydration,並不會影響 SEO!
只是維度更小了!
Page Router 的 SSR 是整個頁面,而 App Router 則細膩到組件層級

也就是說你整個頁面只有Page.ts打 API 拿資料後,接著向下傳的第一個組件就是'use client',就相當於以前寫的getServerSideProps而已
而且還不必拆分成 2 個 function

use server區塊更多些
就像是官方舉例的 markdown render,一些事情提早在 Server 完成,只有將結果送下去 hydration。讓 client 收到的 JS bundle 更小

所以不必對'use client'的使用有太大的壓力。當你覺得需要 hook,那就大方的把相關組件直接標上'use client'吧!
當然還是鼓勵盡可能讓越多範圍 在Server 越佳
因為 Server Component 最大好處就是你可以少處理許多狀態管理的事,只要打 API 取得資料後,return JSX.element 就行了。若是很單純的頁面,反而讓 Code 更加精簡!

參考資料

C.T. Lin / React 新概念 — Server Components
How to handle SEO for client components in Next.js 13?