Create React App を使って作成されるアプリケーションは CSR(Client Side Rendering)で描画されます。CSR の場合、ブラウザは空の HTML をロードした後 JavaScript ファイルをロードしてコンポーネントを描画します。一方 SSR では、最初にサーバーサイド側で静的なページとして HTML を描画し、動的な JavaScript を後から注入します(hydration)。先に静的なページが表示されるため、初期描画の速度が上がります。
ReactDOMServer.renderToString() というメソッドを利用することで、React Component をサーバー上で HTML として扱うことができ、hydrateRoot を利用することでその HTML に JavaScript をアタッチしてインタラクティブな動作を実現できます。
処理手順としは以下のようになります。
具体的な実装を見ていきます。
リポジトリは以下にあります。
https://github.com/Kazuhiro-Mimaki/ssr-with-react-express
まずは最終的に描画したい簡単なコンポーネントを用意します。
importReact,{ useEffect, useState}from"react";exportconstApp=()=>{const[clientMessage, setClientMessage]=useState("");const[count, setCount]=useState(0);useEffect(()=>{setClientMessage("Hello World");},[]);return(<><h1>{clientMessage}</h1><h2>{count}</h2><buttononClick={()=>setCount((prev)=> prev+1)}>+Click</button></>);};exportdefaultApp;次に、hydrate を行います。
React18 以降ではhydrateRoot を使えば、hydrate を行うことができます。
これにより、サーバーで描画されたただの HTML がインタラクティブなものになります。
importReactfrom"react";import{ hydrateRoot}from"react-dom/client";importAppfrom"./App";const container=document.getElementById("root");hydrateRoot(container,<App/>);最後に、サーバーの処理です。
サーバーでは、初期に描画させたいコンポーネントを HTML として用意し、レスポンスします。
importexpressfrom"express";importReactfrom"react";importReactDOMServerfrom"react-dom/server";importAppfrom"../src/App";constPORT= process.env.PORT||4000;const app=express();app.get("/",(req, res)=>{// AppコンポーネントをHTML文字列に変換const app=ReactDOMServer.renderToString(<App/>);// HTMLに変換されたAppコンポーネントを埋め込んだHTMLを作成const html=` <html lang="en"> <head> <script src="client.js" async defer></script> </head> <body> <div>${app}</div> </body> </html>`;// コンポーネントが埋め込まれたHTMLをレスポンス res.send(html);});app.use(express.static("./build"));app.listen(PORT,()=>{console.log(`Server is listening on port${PORT}`);});npm run startを実行して、http://localhost:4000 にアクセスします。Hello World という文字列と count up できる数字が表示されていれば成功です。
バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。
