Closed20

ハイドレーションって何

Yug (やぐ)Yug (やぐ)

なるほど

フロントエンドの世界では、静的な HTML に JavaScript を結び付けてインタラクティブにするプロセスのことを指します。

Yug (やぐ)Yug (やぐ)

JSってブラウザ上でしか動作しないのでは?サーバーがJSを実行するなんてことができるのだろうか?

SSR では、まずブラウザはサーバーに対して、リクエストを送信します。リクエストを受けたサーバーは、JavaScript を実行して、HTML を作り出します。そして、完成したこの HTML をブラウザに送り返します。

あ、普通にNode.jsみたいな「サーバー上で動くJSランタイム」使えばいいのか

Yug (やぐ)Yug (やぐ)

なるほどぉぉ

このとき重要なのは、受け取った HTML が "あくまでも見た目しかない" ということです。つまり、このままだと Web ページは静的で、クリックしても何も起きません。

そこで必要になるのが「Hydration」です。

Hydration とは、先ほど述べたとおり水分補給という意味で、まるで乾いた HTML に、インタラクティブ性という「水」を与えるようなものです。

つまり、先ほどの SSR の例で言うと、HTML を表示した後に、JavaScript をダウンロードして実行するという、このプロセスのことを Hydration と呼びます。

このように、Hydration を行うことで、静的な HTML をインタラクティブな Web ページに変えることが可能になります。

「乾いたHTMLに対する水分補給」という比喩で、水分とは「JSによるインタラクティブ性」のことを指しているのね

んでそのJSはサーバからレスポンスとして渡ってくるものっぽい

Yug (やぐ)Yug (やぐ)

ふむ

ハイドレーションとは?

SSR(サーバーサイドレンダリング) 時代に、どうやって React などに書かれたイベントをフロントエンド側で DOM にアタッチするかの話です。

Yug (やぐ)Yug (やぐ)

ふむふむ

ハイドレーションの流れ

  1. サーバーサイドで HTML が何かしらのデータをもとに作成されるとします

    1. これは実際に HTML に埋め込まれる HTML になります
  2. クライアントサイドに HTML をダウンロードし、 JavaScript のコードたちもダウンロードが終わり実行できる状態になったときに、Component などのコードをもとに HTML を生成し直し、参照透過性のチェックを行う

    1. 参照透過性
      1. 同じ props を渡してるのに違うレンダリングがされるのはダメ
      2. 「サーバーサイドの HTML === クライアントサイドで作った HTML」となるのが期待値
  3. サーバーサイドで生成した HTML にイベントハンドラをアタッチしていきます

  4. このイベントハンドラの紐付けする作業を Hydration と呼称しています

へぇ、参照透過性のチェックも行ってるのか

Yug (やぐ)Yug (やぐ)

なるほど

参照透過性が不一致だった場合の挙動

サーバーサイドの DOM と、クライアントサイドの DOM を比較したときに、一致したら、サーバーサイドのほうの DOM にイベントをアタッチします。
が、不一致だった場合は、クライアントサイドの DOM を使って、 HTML を表示し、イベントをアタッチします。

どういうときに不一致になるかというと、 Math.random() を使うようなランダム要素があり、 props の値は同じなのに、それと Math.random() を使った文字列結合をして、レンダリングを行うと不一致になってしまうなどがあります。

一致したらサーバのdomに、不一致だったらクライアントのdomにイベントアタッチする

Yug (やぐ)Yug (やぐ)

不一致だった場合のデメリット

たしかに

  • デザイン崩れが起きる可能性がある
    • 意図していないデータが紛れ込んでいるということになるため

なるほど、クライアントのdomいじりの方が重いのか。サーバと違ってブラウザに干渉までするからかな?あと、Reactの「仮想DOM自体は重い」というのは確かにそうなのかも、おもしろい

  • パフォーマンスの低下、クライアントで DOM を挿入するので、オーバーヘッドがあります
    • DOM を挿入処理はブラウザ側では一番重い処理
    • React は仮想 DOM が重い問題はあるが、 DOM の挿入処理を最小限にしてパフォーマンスアップを図ったライブラリとして有名になった
Yug (やぐ)Yug (やぐ)

なるほど


上記の図の "Interactive" は、HTML にイベントハンドラがアタッチされて、ユーザーがボタンをクリックすれば、それに応じたアクションが起きる状態です。

CSR の問題点は、"Interactive" の状態になるまでブラウザは真っ白であることです。

Yug (やぐ)Yug (やぐ)

なるほど、一番最初にサーバーからhtml送っちゃう感じかな

この CSR の問題点を解消するために、"Load JS" より手前で、事前に生成された HTML を表示します。

そうすることで、"Interactive" な状態まで真っ白な状態が続くことを避けることができます。

Yug (やぐ)Yug (やぐ)

へぇ!hydration専用のapiがあるのか!
https://ja.react.dev/reference/react-dom/client/hydrateRoot

ほー

引数

  • reactNode: 既存の初期 HTML をレンダーするために使用された “React ノード”。これは通常、ReactDOM Server のメソッド(例:renderToPipeableStream(<App />))でレンダーされた JSX、例えば <App /> になります。

注意点

  • アプリがクライアントでレンダーする形式であり、事前レンダーされた HTML がない場合、hydrateRoot() は使用できません。代わりに createRoot() を使用してください。

hydrateRootはcreateRootにかなり似てる、というか同じ立ち位置っぽいな。createRootのhydartion版みたいな感じか。
https://ja.react.dev/reference/react-dom/client/createRoot

Yug (やぐ)Yug (やぐ)

自分もhydrateRoot使って試してみたら確かに機能した。

だがよくわからんエラーは出てた

Yug (やぐ)Yug (やぐ)

たしかに

hydrateRoot を使うことで、CSR と異なり、"Interactive" な状態になるまで真っ白な状態を防ぐことに成功はしました。ただ、HTML を表示しただけで、"Interactive" な状態ではないため、表示されているボタンを触ったとしても何も起きません。

他の方法っていうのはsuspenseとかbuttonのdisabled属性とかそっち系かな?真っ白にはしないけどローディング中...という表示は出してあげるみたいな

hydrateRoot を使ったとしても "Interactive" な状態になるまでの時間が短くなるわけではないです。
むしろ、表示されているボタンを触ったとしても何も起きないことにイライラするユーザーもいるかもしれません。そのため、"Interactive" な状態になるまでの時間が長すぎる場合は、他の方法で解決を試みる必要があります。

まぁとりあえず通信が発生する以上、Interactiveな状態になるまでの時間はある程度かかることは避けられないんだろうな~

Yug (やぐ)Yug (やぐ)

ん?でもhydrationってJSをアタッチしてinteractiveにすることだよね?hydrationしてる時点でinteractiveになってるはずでは?

ん、あぁinteractiveにはなるんだけどそれまでのラグ(実行/ローディング時間?)は消えることは無いってことかも

Yug (やぐ)Yug (やぐ)

ん、ふと思ったんだが、なんでSSR with hydrationってサーバーからHTML受け取るのにクライアント側でもまたHTML構築するの?

そんでサーバー側で構築したHTMLとクライアント側で構築したHTMLが一致するか調べるという謎の作業をHydrationはするけど、

クライアント側で構築するっていうのがわからん。サーバーからHTML貰えるんだからそれそのまま表示すればいいじゃん...ていう疑問。

Yug (やぐ)Yug (やぐ)

あーわかったかも

HTMLパースの勉強してたときのことを思い出した。HTMLってサーバーから来るときビットになっちゃうから結局再構築が必要ってだけかも。

当然の話か...

んでその中間表現として、サーバーから来るのは実DOM、クライアントで作るのは仮想DOM、で一応一致するか確認するよ、みたいな流れか

サーバから来たHTMLをもとに作られた実DOM が ①
クライアント側でReactが生成した仮想DOM が② ※画面にレンダリングされていない

https://zenn.dev/jordan23/articles/05b78f925d35ba#またハイドレーションによってhtmlをインタラクティブなものにするだけではなく、

あと、そのままHTMLを再構築するだけって訳でもなくて、それだとinteractiveにできないから、やはりReactが制御下におけるようにちゃんと自分好みに仮想DOMで再構築しなおしてHydrationもしっかりする、ていうことが必要だという理由もあるかも

Yug (やぐ)Yug (やぐ)

ん?いやでも仮想DOM構築より先に、受け取ったHTMLをそのまま使用してDOM構築して画面描画だけ先にしちゃうっぽいな

  1. クライアント側で DOM の構築と表示
    • サーバーから受け取った HTML を基に DOM を構築し、描画処理を行う
    • この時点において、ページにはインタラクティブ性はないが、すぐにユーザに見える形で表示される
  2. バンドル JS の実行と仮想 DOM の構築
    • バンドル JS を基に仮想 DOM を構築する
    • 構築した仮想 DOM と実 DOM に不一致がないか比較する

https://zenn.dev/yuu104/articles/react-server-component#処理の流れ-1

そのあと仮想DOM構築らしい

てことはHydrationより前に静的HTML表示だけしておくってことか

そのあとHydration(一致確認 & アタッチ)することでそのHTMLがinteractiveになる

このスクラップは2025/01/24にクローズされました