avatar
instagramthreads

白話理解 Hydration、Streaming 渲染模式

SSR with Hydration

先簡單說明下什麼是 hydration,中文有人翻作水合、水化、補水,也可以理解為脫水的反義詞。在渲染模式中 hydration 就像用互動性與 event handlers 對靜態 HTML 去澆水,去綁定對應 DOM node 讓它們能成為可互動的元件。

而 SSR with hydration 是為了改善傳統 SSR 互動體驗較差的問題,而產生這樣的「第一屏交給 server,後續交由 client 處理互動」的渲染模式,因實作上較困難,開發時通常會直接使用 meta framework 如 Next.js 或 Nuxt.js 處理。

3body rehydration

流程

  • 使用者輸入網址
  • server 收到請求後將渲染好的靜態 HTML 回傳
  • 瀏覽器接著下載 JS,並交由 client side 逐步將已載好的 JS 對靜態 HTML 做 hydration

優點

  • 相比傳統 SSR 有更快的 FCP
  • SEO 友善

缺點

  • TTI 較差,因需等 JS 載完後才可互動
  • 為了解決整份 JS 載入、執行時間過長導致的體驗差問題,在 React 中也衍伸出了漸進式水合、選擇性水合等模式

參考如上影片中,一開始小區塊的 header 其實已經準備好,但苦於要等待 Comments 區塊載完,所以會有一段時間的白屏。

Selective Hydration

為了解決上述整份 JS 載入、執行時間過長導致的體驗差問題,在 React 中也衍伸出了漸進式水合、選擇性水合等模式。而在進入 hardcore 的技術解析前,從 React Conf 2021 的影片中找到一個很白話的範例在講解這段。

想像一下今天父母帶著小孩去餐廳用餐時,如果一起點餐並等待全部一起上菜,可能會因為等待時間太久,導致小孩會各種哭鬧造成大家用餐體驗不佳。有個好解法就是特別安排一個幼兒桌,大人小孩分開點餐,如此可以讓服務生先 serve 比較容易準備的小孩餐點讓他們先吃到,就不會吵鬧。而這件事就是所謂的 Selective Hydration 想做的事。

react conf 2021 selective hydration

Streaming

在理解 selective hydration 前,需先了解 Streaming 這個機制。Streaming 的背後是一個關於 HTTP 分塊傳輸編碼 (Chunked Transfer-Encoding) 的機制,有興趣深入研究的話可以參考這篇文章講得蠻清楚的。

簡單理解的話,有別於一次傳一大包 payload,它能讓 server 持續對 client side 傳輸 small chunk 資料。

而當渲染模式中套用了 streaming 的傳輸機制時,就能讓 client side 畫面在收到這些小塊時,更快地反應去渲染畫面。在 React v18 中完善了 streaming 相關的 API,讓底層有辦法全面支援 Suspense 機制,有興趣可以參考 Dan 這篇經典的說明深入研究,因為這段其實 Next.js 都幫忙做好了,這裡先了解基本就好,下面回到選擇性水合的部分。

Selective Hydration 流程

參考下面這個範例,可以先假設 <Comments /> 這個負責留言的區塊,因為 server 可能遇到各種拉資料會花比較久時間的因素,為避免阻塞到其他靜態區塊渲染,此時可以先用 Suspense 包起來:

import { Suspense, lazy } from 'react';
import Loader from './Loader';

const Comments = lazy(() => import('./Comments'));

function App() {
  return (
    <main>
      <Header />
      <Suspense fallback={<Loader />}>
        <Comments />
      </Suspense>
      <Footer />
    </main>
  );
}

如此處理就能讓渲染時,先把可提前送到 client side 的內容傳過去,而評論區塊則用載入樣式先佔位,可能講得有點複雜,讓我們看看下方影片示意:

與此同時,server 能同時在與外部 server 拉資料,但剛剛的其他部分就可以先做 hydration,此即為 selective hydration 的原理:

等到評論資料回來後,server 會再透過 streaming 的方式傳給 client side,取代原本的 loading 樣式,並做完剩下的 hydration。這裡的評論區塊就像前面提到的大人桌一樣,而 header 等靜態區塊就像小孩桌。

另外有個值得一提的是 Addy 這本書中及 patterns.dev 網站上還有提到一個模式是 Progressive Hydration,但我自己消化後覺得這像是 React v16.6 ~ v18 過渡階段中的產物,因為當初未能完全支援 Suspense 模式,可以說如今的選擇性水合甚至後來的島嶼架構都是站在巨人的肩膀上去演化出來的。

優點

解決了過往傳統 hydration with SSR 的 Uncanny Valley 問題(hydration 未完成前無法互動),而達成更快的 FCP、TTI

缺點

沒仔細查其他資料,但目前的理解應該是學習曲線較高,若有其他觀點也歡迎留言分享

References

此文章部分內容與影片素材引用自 Patterns.dev 如下:

根據 Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 授權協議分享。詳細資訊請見 CC BY-NC 4.0,如有任何問題,歡迎留言或來信告知,感謝。

延伸閱讀

Previous Article

ISR