iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0
Modern Web

深入淺出,完整認識 Next.js 13 !系列 第 10

Day 10 - Server Components 是什麼?跟 Server Side Rendering 一樣嗎?

  • 分享至 

  • xImage
  •  

前兩天介紹了 Rendering Infrastructure 優化的其中兩個項目 - 新的路由架構 App Router 與使用 layout.tsx 更簡單地實現 persistant layout,假如還沒看的朋友可參考 Day 08Day 09 的文章!

今天來看最後一個項目 - Server Components。


Server Components 顧名思義,即是在 server-side 渲染的 components ( 這邊的 server 其實用 “React Server” 會更精準,後面會談到 )。事實上,Server Components 並不是由 Vercel 推出的新概念,而是 React 早在 2020 即推出的功能 ( 可參考 React Blog 公告 )。只是如同 Day 02 提到, 假如單純用 React 專案實作 React Server Components ( RSC ),你需要起 server、做一些 bundler 的設定等等。而 App Router 則是預設 components 是 Server Components,假如要轉為 Client Components 只需要在檔案最上方標示 ‘use client’ 即可,大幅降低 RSC 的使用門檻。

舉例來說,假如我們有個靜態頁面,要渲染一支 API 的資料,傳統的做法可能是將渲染內容存在一個 state 中,用 useEffect 打 API,拿到 API response 後更新 state 觸發 re-render 更新頁面內容:

import { useEffect, useState } from 'react';

export default function Home() {
  const [apiData, setApiData] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async (url: string) => {
      try {
        const response = await fetch(url);
        const jsonData = await response.json();
        setApiData(jsonData);
      } catch (err) {
        window.alert('發生錯誤,請稍後再試');
      }
    };

    fetchData('http://localhost:3000/api/hello-world');
  }, []);

  return <div>{apiData}</div>;
}

假如使用 Server Components 來產生一樣的靜態網頁,我們可以在 server-side fetch 完資料後,直接以 response 內容渲染 components:

const fetchData = async (url: string) => {
  try {
    const response = await fetch(url);
    return await response.json();
  } catch (err) {
    return null;
  }
};

export default async function Home() {
  const apiData = await fetchData('http://localhost:3000/api/hello-world');

  return <div>{apiData}</div>;
}

那使用 Server Components 有什麼好處與限制呢?我們留到明天再和大家分享!再進入實作講解前,想花點時間回答兩個常見的問題:

  1. Client Components 只能在 Client-Side 渲染嗎?
  2. Server Components 與 Server-Side Rendering 一樣嗎?

Client Components 只能在 Client-Side 渲染嗎 ?

答案是錯!Client Components 也可以在 server-side 渲染。事實上,Client Components 和 Server Components 的 Client 與 Server,更精準來說,指的是 "React Client” 與 "React Server”,跟我們在講 Client-Side Rendering 與 Server-Side Rendering 中的 Client 和 Server 不太一樣。

等等,那什麼是 React Server 和 React Client?

React Server 和 React Client 並不是一個具有「實體」的服務或設備,像是實體 server 或瀏覽器,而是 React 渲染過程中兩個角色。

簡單來說,React Server 指的是渲染 RSC 的環境,預設會在 build time 運行,但也可以跑在實體 server上,主要任務是與後端服務互動,並渲染 RSC;而 React Client 指的是接受與使用 React Server output 的環境,server 和瀏覽器上都可以運行。假如跑在瀏覽器上,主要任務會是操控 DOM;跑在 server 上則是產生初始的 HTML string ( SSR ),而在 React Client 被渲染的 components 就稱為 Client Components。

除此之外,RSC 的概念並不是產生一個全新的 components 運作模式,而是在既有的架構中加入一層 RSC Server layer,來與後端互動,生成完 Server Components 後再將 components 以 props 傳給 React Client,進而接著產生 HTML。
RSC 示意圖
( 圖片來源:https://github.com/reactwg/server-components/discussions/4 )

所以 Client Components 可以在 server-side 渲染也可以在 client-side 渲染。App Router 中,假如用戶第一次拜訪網頁,Client Components 會在 server-side render 一個靜態 HTML,再到 client-side 執行 hydration,加快網頁初始載入的速度;假如是非第一次拜訪的 navigation,代表 JavaScript bundle 已經下載並 parse 完了,Client Components 就會完全在 client-side 渲染。

Server Components 與 Server-Side Rendering 一樣嗎?

兩者是不同的!兩者執行的階段和最終的 output 不同。

Server-Side Rendering ( SSR ) 是在 React Client 中,根據你的程式碼產生一份 HTML string。套用 Dan Abramov 在 React Server Components deep dive 中一句定義 SSR 的話:

Turning JSX into an HTML string is usually known as "Server-Side Rendering" (SSR).

而 Sever Components 則是在 React Server 中生成出 React components ( 一個 object )。這些 components 生成後,會被傳到 React Client 產生 HTML,所以 Server Components 是在 SSR 之前即生成。這也意味著走 SSR 不一定要用 Server Components,用 Server Components 也不一定要走 SSR

假如想更深入了解 Server Components 是如何生成的,可以參考 Dan 寫的 deep dive
initial load
( 圖片來源:https://github.com/reactwg/server-components/discussions/5 )

總結來說,小弟個人覺得 Client Components 這個命名蠻容易誤導大家,以為 Client Components 就是在 client-side 渲染的 components。但如同上文提到,Client Components 也可以在 server-side 渲染。

那該怎麼更簡單地區分 Client Components 與 Server Components 呢?我認為可以說 Client Components 是可以使用 client-only 功能 (ex: event listener、react hooks、local storage...) 的 components;Server Components 則是沒有要使用這些功能,而且可以在頁面渲染前,在 server 提前做一些事 ( ex: 到 DB 撈資料 ) 的 components。這部分不用著急,等看完後面幾天的文章後,可能會更好理解!

小補充,也因為 client-only 的功能通常具備互動性,我也有看到國外網友建議 Client Components 改名叫 Interact Components 之類的,我們就靜觀 Vercel 之後會不會調整名稱。

今天介紹了 Server Components 是什麼,以及釐清 SSR 與 Server Components 的關係與差異,明天會接著和大家分享 Server Components 的優點、使用時機和限制。

謝謝大家耐心的閱讀,我們明天見!


上一篇
Day 09 - Persistent Layout 是什麼?要怎麼在 App Router 中實踐?
下一篇
Day 11 - Next.js 13 App Router :什麼時候適合使用 Server Components 或 Client Components?
系列文
深入淺出,完整認識 Next.js 13 !30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ceall8650
iT邦新手 4 級 ‧ 2024-09-08 15:50:48

Hi S.C 您好,

閱讀完您的文章後, 對於在server side 渲染client components有些疑問
以App Router 的情況來說, 只有加上"use client" 的元件, 才會被視為 client component

以文件上來說
https://nextjs.org/docs/app/building-your-application/rendering/client-components#full-page-load

在server side渲染client components的狀況
是否指的就是產生HTML 文件嗎? 或是會將Client component內, 不需要與user互動的部分也會先提前渲染?

S.C iT邦新手 4 級 ‧ 2024-09-19 14:27:04 檢舉

哈囉~感謝你的留言!

你的理解沒有錯喔,這邊的「渲染」簡單來說就是產生初始的 HTML 檔案沒錯。假如專案是 server-side rendering,在渲染 client components 時,會先在初始 HTML 給予一個預設值 ( ex: React state 的初始值 ),動態事件再由瀏覽器執行 javascript 處理 ( hydration )

ceall8650 iT邦新手 4 級 ‧ 2024-09-19 23:08:03 檢舉

感謝您的回覆.

所以以我的理解, 整個流程跟角色會像是

  1. 在CI server中build檔案, CI server 會是React server, 而build的檔案是React Server component
  2. 當user訪問網站時, web server收到網站檔案的請求, web server 產生HTML(server-side redering), 並回傳給client, 此時web server是React client, 而HTML 是 client components
  3. Client端 收到HTML, JS, CSS..., 會顯示畫面但無法互動, 等JS被瀏覽器解析完之後, 產生可以互動的UI 元件(client-side rendering), user 才能操作畫面

不知道這樣的理解是否跟您的文章所提到的名詞跟流程是否一樣

我要留言

立即登入留言