ユーザーの好みにシームレスに適応し、明るいテーマ、暗いテーマ、システムベースのテーマを簡単に切り替えることができる Web サイトにアクセスするところを想像してみてください。
この記事は、React を使用した SSR に関するシリーズの続きです。基本的な記事では、本番環境にすぐに使える構成を検討し、高度なテクニックでは、ハイドレーション エラーなどの課題に取り組みました。ここで、SSR とシームレスに統合する堅牢なテーマ サポートを実装することで、さらに一歩進めます。
目次
- テーマとSSR
-
実装
- 依存関係をインストールする
- サーバー ビルドに Cookie を追加します
- サーバーにテーマを適用します
- クライアントでテーマを処理する
- 結論
テーマとSSR
主な問題は、間違ったテーマの初期フラッシュ (FOIT)です。
基本的に、テーマは CSS 変数を変更するだけです。ほとんどの場合、次の 3 つのテーマを使用します:
- Light: CSS 変数のデフォルトのセット。
- Dark: の場合に適用されます。タグにはダーククラスが含まれています。
- システム: (prefers-color-scheme: dark) を使用して、ユーザーのシステム設定に基づいて自動的に切り替えます。 テーマを暗いか明るいかを決定するためのメディア クエリ。
デフォルトでは、サーバーはライトテーマで HTML をレンダリングし、ブラウザに送信します。ユーザーがダーク テーマを好む場合、最初のページの読み込み時に目に見えるテーマの変更が表示され、ユーザー エクスペリエンスが中断されます。
この問題を解決するには、主に 2 つの方法があります:
- を追加します。サーバー上の HTML にタグを追加し、クライアント上でクラスを動的に設定します。
- Cookie を使用してユーザーのテーマ設定を保存し、サーバー上にクラスを設定します。
最初の解決策は、次のテーマ パッケージがどのように機能するかです (2025 年 1 月)。この記事では、Cookie ベースのアプローチを実装して、SSR アプリケーションでテーマをシームレスに処理できるようにします。
実装
テーマを実装するには、2 つの Cookie を使用します:
- serverTheme - 正しいクラスを に適用するために使用されます。タグ。
- clientTheme - ハイドレーション エラーを処理するために使用されます。
クライアントは常に両方の Cookie を設定し、サーバーが次のリクエストで適切なテーマを正しくレンダリングできるようにします。
このガイドは、下部にリンクされている前の記事 本番対応 SSR React アプリケーションの構築 で紹介した概念に基づいて構築されています。わかりやすくするために、共有定数と型はここでは作成されませんが、それらの実装はサンプル リポジトリで見つけることができます。
依存関係のインストール
Cookie の処理に必要なパッケージをインストールします:
pnpm add cookie js-cookie
js-cookie のインストール タイプ:
pnpm add -D @types/js-cookie
アプリで React-router を使用していない場合は、Cookie パッケージを devDependency として使用できます。
サーバー ビルドに Cookie を追加する
tsup 構成ファイルを更新します:
// ./tsup.config.ts import { defineConfig } from 'tsup' export default defineConfig({ entry: ['server'], outDir: 'dist/server', target: 'node22', format: ['cjs'], clean: true, minify: true, external: ['lightningcss', 'esbuild', 'vite'], noExternal: [ 'express', 'sirv', 'compression', 'cookie', // Include the cookie in the server build ], })
サーバーにテーマを適用する
テーマ定数の定義
// ./server/constants.ts export const CLIENT_THEME_COOKIE_KEY = 'clientTheme' export const SERVER_THEME_COOKIE_KEY = 'serverTheme' export enum Theme { Light = 'light', Dark = 'dark', System = 'system' }
テーマクラスをタグに適用
正しいテーマ クラスを に適用するユーティリティ関数を作成します。 serverTheme cookie に基づくタグ:
// ./server/lib/applyServerTheme.ts import { parse } from 'cookie' import { Request } from 'express' import { SERVER_THEME_COOKIE_KEY, Theme } from '../constants' export function applyServerTheme(req: Request, html: string): string { const cookies = parse(req.headers.cookie || '') const theme = cookies?.[SERVER_THEME_COOKIE_KEY] if (theme === Theme.Dark) { return html.replace('', ` <h4> Retrieve the Client Theme Cookie </h4> <p>Create a utility function to retrieve the clientTheme cookie<br> </p> <pre class="brush:php;toolbar:false">// ./server/getClientTheme.ts import { parse } from 'cookie' import { Request } from 'express' import { CLIENT_THEME_COOKIE_KEY, Theme } from '../constants' export function getClientTheme(req: Request) { const cookies = parse(req.headers.cookie || '') return cookies?.[CLIENT_THEME_COOKIE_KEY] as Theme | undefined }
テーマ設定用のサーバー構成を更新する
開発構成:
// ./server/dev.ts import fs from 'fs' import path from 'path' import { Application } from 'express' import { HTML_KEY } from './constants' import { applyServerTheme } from './lib/applyServerTheme' import { getClientTheme } from './lib/getClientTheme' const HTML_PATH = path.resolve(process.cwd(), 'index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'src/entry-server.tsx') export async function setupDev(app: Application) { const vite = await ( await import('vite') ).createServer({ root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', }) app.use(vite.middlewares) app.get('*', async (req, res, next) => { try { let html = fs.readFileSync(HTML_PATH, 'utf-8') html = await vite.transformIndexHtml(req.originalUrl, html) const { render } = await vite.ssrLoadModule(ENTRY_SERVER_PATH) // send Client Theme from cookie to render const appHtml = await render(getClientTheme(req)) // Apply Server theme on template html html = applyServerTheme(req, html) html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { vite.ssrFixStacktrace(e as Error) console.error((e as Error).stack) next(e) } }) }
実稼働構成:
// ./server/prod.ts import fs from 'fs' import path from 'path' import compression from 'compression' import { Application } from 'express' import sirv from 'sirv' import { HTML_KEY } from './constants' import { applyServerTheme } from './lib/applyServerTheme' import { getClientTheme } from './lib/getClientTheme' const CLIENT_PATH = path.resolve(process.cwd(), 'dist/client') const HTML_PATH = path.resolve(process.cwd(), 'dist/client/index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'dist/ssr/entry-server.js') export async function setupProd(app: Application) { app.use(compression()) app.use(sirv(CLIENT_PATH, { extensions: [] })) app.get('*', async (req, res, next) => { try { let html = fs.readFileSync(HTML_PATH, 'utf-8') const { render } = await import(ENTRY_SERVER_PATH) // send Client Theme from cookie to render const appHtml = await render(getClientTheme(req)) // Apply Server theme on template html html = applyServerTheme(req, html) html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { console.error((e as Error).stack) next(e) } }) }
クライアント上でテーマを処理する
定数の定義
クライアントが使用する定数を複製するか、共有フォルダーに移動します
// ./src/constants.ts export const SSR = import.meta.env.SSR export const CLIENT_THEME_COOKIE_KEY = 'clientTheme' export const SERVER_THEME_COOKIE_KEY = 'serverTheme' export enum Theme { Light = 'light', Dark = 'dark', System = 'system', }
テーマコンテキストの作成
React コンテキストを設定してテーマの状態を管理し、テーマ管理メソッドを提供します。
// ./src/theme/context.ts import { createContext, useContext } from 'react' import { Theme } from '../constants' export type ThemeContextState = { theme: Theme setTheme: (theme: Theme) => void } export const ThemeContext = createContext<themecontextstate>({ theme: Theme.System, setTheme: () => null, }) export const useThemeContext = () => useContext(ThemeContext) </themecontextstate>
テーマユーティリティの実装
// ./src/theme/lib.ts import Cookies from 'js-cookie' import { CLIENT_THEME_COOKIE_KEY, SERVER_THEME_COOKIE_KEY, SSR, Theme } from '../constants' // Resolve the system theme using the `prefers-color-scheme` media query export function resolveSystemTheme() { if (SSR) return Theme.Light return window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.Dark : Theme.Light } // Update the theme cookies and set appropriate class to export function updateTheme(theme: Theme) { if (SSR) return const resolvedTheme = theme === Theme.System ? resolveSystemTheme() : theme Cookies.set(CLIENT_THEME_COOKIE_KEY, theme) Cookies.set(SERVER_THEME_COOKIE_KEY, resolvedTheme) window.document.documentElement.classList.toggle('dark', resolvedTheme === Theme.Dark) } // Get the default theme from cookies export function getDefaultTheme(): Theme { if (SSR) return Theme.System const theme = (Cookies.get(CLIENT_THEME_COOKIE_KEY) as Theme) || Theme.System updateTheme(theme) return theme }
テーマプロバイダーを作成する
// ./src/theme/Provider.tsx import { PropsWithChildren, useState } from 'react' import { Theme } from '../constants' import { ThemeContext } from './context' import { getDefaultTheme, updateTheme } from './lib' type Props = PropsWithChildren & { defaultTheme?: Theme // Handle theme for SSR } export function ThemeProvider({ children, defaultTheme }: Props) { const [theme, setTheme] = useState<theme>(defaultTheme || getDefaultTheme()) const handleSetTheme = (theme: Theme) => { setTheme(theme) updateTheme(theme) } return <themecontext value="{{" theme settheme: handlesettheme>{children}</themecontext> } </theme>
// ./src/theme/index.ts export { ThemeProvider } from './Provider' export { useThemeContext } from './context'
コンポーネントでテーマコンテキストを使用する
// ./src/App.tsx import reactLogo from './assets/react.svg' import viteLogo from '/vite.svg' import Card from './Card' import { Theme } from './constants' import { ThemeProvider } from './theme' import './App.css' // Theme from Server Entry type AppProps = { theme?: Theme } function App({ theme }: AppProps) { return ( <themeprovider defaulttheme="{theme}"> <div> <a href="https://vite.dev" target="_blank" rel="noreferrer"> <img src="%7BviteLogo%7D" classname="logo" alt="Vite logo"> </a> <a href="https://react.dev" target="_blank" rel="noreferrer"> <img src="%7BreactLogo%7D" classname="logo react" alt="React logo"> </a> </div> <h1 id="Vite-React">Vite + React</h1> <card></card> <p classname="read-the-docs">Click on the Vite and React logos to learn more</p> </themeprovider> ) } export default App
カードコンポーネントの作成
// ./src/Card.tsx import { useState } from 'react' import { Theme } from './constants' import { useThemeContext } from './theme' function Card() { const { theme, setTheme } = useThemeContext() const [count, setCount] = useState(0) return ( <div classname="card"> <button onclick="{()"> setCount((count) => count + 1)}>count is {count}</button> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> <div> Themes:{' '} <select value="{theme}" onchange="{(event)"> setTheme(event.target.value as Theme)}> <option value="{Theme.System}">System</option> <option value="{Theme.Light}">Light</option> <option value="{Theme.Dark}">Dark</option> </select> </div> </div> ) } export default Card
ハイドレーションエラーを解決する
テーマをサーバーのレンダリング メソッドに渡し、サーバーで生成された HTML がクライアント側のレンダリングと一致することを確認します。
import { renderToString } from 'react-dom/server' import App from './App' import { Theme } from './constants' export function render(theme: Theme) { return renderToString(<app theme="{theme}"></app>) }
スタイルの追加
:root { color: #242424; background-color: rgba(255, 255, 255, 0.87); } :root.dark { color: rgba(255, 255, 255, 0.87); background-color: #242424; }
結論
この記事では、SSR React アプリケーションにシームレスなテーマを実装するという課題に取り組みました。 Cookie を使用し、クライアント側とサーバー側の両方のロジックを統合することにより、ハイドレーション エラーやユーザー エクスペリエンスを中断することなく、明るいテーマ、暗いテーマ、およびシステムベースのテーマをサポートする堅牢なシステムを作成しました。
コードを探索する
- 例: 反応-ssr-テーマ-例
- SSR で着陸: プロフェッショナル着陸
関連記事
これは、React を使用した SSR に関するシリーズの一部です。他の記事もお楽しみに!
- 本番環境に対応した SSR React アプリケーションを構築する
- ストリーミングおよび動的データを使用した高度な React SSR テクニック
- SSR React アプリケーションでのテーマのセットアップ
つながりを保つ
フィードバック、コラボレーション、技術的なアイデアについての議論はいつでも受け付けています。お気軽にご連絡ください。
- ポートフォリオ: maxh1t.xyz
- メール: m4xh17@gmail.com
以上がSSR React アプリケーションでのテーマのセットアップの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

PythonとJavaScriptの主な違いは、タイプシステムとアプリケーションシナリオです。 1。Pythonは、科学的コンピューティングとデータ分析に適した動的タイプを使用します。 2。JavaScriptは弱いタイプを採用し、フロントエンドとフルスタックの開発で広く使用されています。この2つは、非同期プログラミングとパフォーマンスの最適化に独自の利点があり、選択する際にプロジェクトの要件に従って決定する必要があります。

PythonまたはJavaScriptを選択するかどうかは、プロジェクトの種類によって異なります。1)データサイエンスおよび自動化タスクのPythonを選択します。 2)フロントエンドとフルスタック開発のためにJavaScriptを選択します。 Pythonは、データ処理と自動化における強力なライブラリに好まれていますが、JavaScriptはWebインタラクションとフルスタック開発の利点に不可欠です。

PythonとJavaScriptにはそれぞれ独自の利点があり、選択はプロジェクトのニーズと個人的な好みに依存します。 1. Pythonは、データサイエンスやバックエンド開発に適した簡潔な構文を備えた学習が簡単ですが、実行速度が遅くなっています。 2。JavaScriptはフロントエンド開発のいたるところにあり、強力な非同期プログラミング機能を備えています。 node.jsはフルスタックの開発に適していますが、構文は複雑でエラーが発生しやすい場合があります。

javascriptisnotbuiltoncorc;それは、解釈されていることを解釈しました。

JavaScriptは、フロントエンドおよびバックエンド開発に使用できます。フロントエンドは、DOM操作を介してユーザーエクスペリエンスを強化し、バックエンドはnode.jsを介してサーバータスクを処理することを処理します。 1.フロントエンドの例:Webページテキストのコンテンツを変更します。 2。バックエンドの例:node.jsサーバーを作成します。

PythonまたはJavaScriptの選択は、キャリア開発、学習曲線、エコシステムに基づいている必要があります。1)キャリア開発:Pythonはデータサイエンスとバックエンド開発に適していますが、JavaScriptはフロントエンドおよびフルスタック開発に適しています。 2)学習曲線:Python構文は簡潔で初心者に適しています。 JavaScriptの構文は柔軟です。 3)エコシステム:Pythonには豊富な科学コンピューティングライブラリがあり、JavaScriptには強力なフロントエンドフレームワークがあります。

JavaScriptフレームワークのパワーは、開発を簡素化し、ユーザーエクスペリエンスとアプリケーションのパフォーマンスを向上させることにあります。フレームワークを選択するときは、次のことを検討してください。1。プロジェクトのサイズと複雑さ、2。チームエクスペリエンス、3。エコシステムとコミュニティサポート。

はじめに私はあなたがそれを奇妙に思うかもしれないことを知っています、JavaScript、C、およびブラウザは正確に何をしなければなりませんか?彼らは無関係であるように見えますが、実際、彼らは現代のウェブ開発において非常に重要な役割を果たしています。今日は、これら3つの間の密接なつながりについて説明します。この記事を通して、JavaScriptがブラウザでどのように実行されるか、ブラウザエンジンでのCの役割、およびそれらが協力してWebページのレンダリングと相互作用を駆動する方法を学びます。私たちは皆、JavaScriptとブラウザの関係を知っています。 JavaScriptは、フロントエンド開発のコア言語です。ブラウザで直接実行され、Webページが鮮明で興味深いものになります。なぜJavascrを疑問に思ったことがありますか


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

SecLists
SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター
