Rumah hujung hadapan web tutorial js Menyediakan Tema dalam Aplikasi SSR React

Menyediakan Tema dalam Aplikasi SSR React

Jan 06, 2025 am 12:56 AM

Setting Up Themes in SSR React Applications

Bayangkan melawati tapak web yang menyesuaikan dengan pilihan anda dengan lancar—bertukar antara tema terang, gelap dan berasaskan sistem dengan lancar.

Artikel ini meneruskan siri saya tentang SSR dengan React. Dalam artikel asas, kami meneroka konfigurasi sedia pengeluaran, manakala dalam teknik lanjutan, kami menangani cabaran seperti ralat penghidratan. Kini, kami akan melangkah lebih jauh dengan melaksanakan sokongan tema teguh yang disepadukan dengan lancar dengan SSR.

Jadual Kandungan

  • Tema dan SSR
  • Perlaksanaan
    • Pasang Ketergantungan
    • Tambahkan kuki pada Binaan Pelayan
    • Gunakan Tema pada Pelayan
    • Kendalikan Tema pada Pelanggan
  • Kesimpulan

Tema dan SSR

Isu utama ialah Denyar Awal Tema Salah (FOIT).

Pada asasnya, tema hanyalah tentang menukar pembolehubah CSS. Dalam kebanyakan kes, anda akan menggunakan tiga tema:

  • Cahaya: Set lalai pembolehubah CSS.
  • Gelap: Digunakan apabila tag mempunyai kelas gelap.
  • Sistem: Bertukar secara automatik berdasarkan pilihan sistem pengguna, menggunakan (skim-warna-pilihan: gelap) pertanyaan media untuk menentukan sama ada tema harus gelap atau terang.

Secara lalai, pelayan akan memaparkan HTML dengan tema ringan dan menghantarnya ke penyemak imbas. Jika pengguna lebih suka tema gelap, mereka akan melihat perubahan tema yang boleh dilihat pada pemuatan halaman pertama, yang mengganggu pengalaman pengguna.

Terdapat dua cara utama untuk menyelesaikan isu ini:

  • Tambahkan teg dalam HTML pada pelayan dan tetapkan kelas secara dinamik pada klien.
  • Gunakan kuki untuk menyimpan pilihan tema pengguna dan tetapkan kelas pada pelayan.

Penyelesaian pertama ialah cara pakej tema seterusnya berfungsi (Jan 2025). Dalam artikel ini, anda akan melaksanakan pendekatan berasaskan kuki untuk memastikan pengendalian tema yang lancar dalam aplikasi SSR anda.

Perlaksanaan

Untuk melaksanakan tema, anda akan menggunakan dua kuki:

  1. serverTheme - Digunakan untuk menggunakan kelas yang betul pada tag.
  2. Tema pelanggan - Digunakan untuk mengendalikan ralat penghidratan.

Pelanggan sentiasa menetapkan kedua-dua kuki, memastikan pelayan dapat memaparkan tema yang sesuai dengan betul pada permintaan seterusnya.

Panduan ini dibina berdasarkan konsep yang diperkenalkan dalam artikel sebelumnya, Membina Aplikasi Reaksi SSR Sedia Pengeluaran, yang boleh anda temui dipautkan di bahagian bawah. Untuk kesederhanaan, pemalar dan jenis yang dikongsi tidak dibuat di sini, tetapi anda boleh menemui pelaksanaannya dalam repositori contoh.

Pasang Ketergantungan

Pasang pakej yang diperlukan untuk pengendalian kuki:

pnpm add cookie js-cookie
Salin selepas log masuk

Jenis pemasangan untuk js-cookie:

pnpm add -D @types/js-cookie
Salin selepas log masuk

Jika anda tidak menggunakan penghala tindak balas dalam apl anda, anda boleh menggunakan pakej kuki sebagai devDependencies.

Tambahkan kuki pada Binaan Pelayan

Kemas kini fail konfigurasi tsup anda:

// ./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
  ],
})
Salin selepas log masuk

Gunakan Tema pada Pelayan

Tentukan Pemalar Tema

// ./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'
}
Salin selepas log masuk

Gunakan Kelas Tema untuk Teg

Buat fungsi utiliti untuk menggunakan kelas tema yang betul pada tag berdasarkan kuki Tema pelayan:

// ./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('<html lang="en">', `<html lang="en">



<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
}
Salin selepas log masuk

Kemas kini Konfigurasi Pelayan untuk Tema

Tatarajah Pembangunan:

// ./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)
    }
  })
}
Salin selepas log masuk

Konfigurasi Pengeluaran:

// ./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)
    }
  })
}
Salin selepas log masuk

Mengendalikan Tema pada Klien

Tentukan Pemalar

Pemalar pendua untuk kegunaan pelanggan atau alihkannya ke folder kongsi

// ./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',
}
Salin selepas log masuk

Cipta Konteks Tema

Sediakan konteks React untuk mengurus keadaan tema dan menyediakan kaedah pengurusan tema:

// ./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)
Salin selepas log masuk

Laksanakan Utiliti Tema

// ./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 <html>
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
}
Salin selepas log masuk

Buat Penyedia Tema

// ./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>
}
Salin selepas log masuk
// ./src/theme/index.ts
export { ThemeProvider } from './Provider'
export { useThemeContext } from './context'
Salin selepas log masuk

Gunakan Konteks Tema dalam Komponen

// ./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={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank" rel="noreferrer">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <Card />
      <p className="read-the-docs">Click on the Vite and React logos to learn more</p>
    </ThemeProvider>
  )
}

export default App
Salin selepas log masuk

Buat Komponen Kad

// ./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
Salin selepas log masuk

Selesaikan Ralat Penghidratan

Lepaskan tema kepada kaedah pemaparan pelayan untuk memastikan HTML yang dijana pelayan sepadan dengan pemaparan sebelah klien:

import { renderToString } from 'react-dom/server'
import App from './App'
import { Theme } from './constants'

export function render(theme: Theme) {
  return renderToString(<App theme={theme} />)
}
Salin selepas log masuk

Tambah Gaya

:root {
    color: #242424;
    background-color: rgba(255, 255, 255, 0.87);
}

:root.dark {
    color: rgba(255, 255, 255, 0.87);
    background-color: #242424;
}
Salin selepas log masuk

Kesimpulan

Dalam artikel ini, kami menangani cabaran untuk melaksanakan tema lancar dalam aplikasi SSR React. Dengan menggunakan kuki dan menyepadukan kedua-dua logik sisi klien dan sisi pelayan, kami mencipta sistem yang teguh yang menyokong tema terang, gelap dan berasaskan sistem tanpa ralat penghidratan atau gangguan pengalaman pengguna.

Terokai Kod

  • Contoh: react-ssr-themes-example
  • Mendarat dengan SSR: pendaratan profesional

Artikel Berkaitan

Ini adalah sebahagian daripada siri saya tentang SSR dengan React. Nantikan lebih banyak artikel!

  • Membina Aplikasi SSR React Sedia Pengeluaran
  • Teknik SSR React Terperinci dengan Penstriman dan Data Dinamik
  • Menyediakan Tema dalam Aplikasi SSR React

Kekal Terhubung

Saya sentiasa terbuka untuk maklum balas, kerjasama atau membincangkan idea teknologi — jangan ragu untuk menghubungi kami!

  • Portfolio: maxh1t.xyz
  • E-mel: m4xh17@gmail.com

Atas ialah kandungan terperinci Menyediakan Tema dalam Aplikasi SSR React. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

<🎜>: Bubble Gum Simulator Infinity - Cara Mendapatkan dan Menggunakan Kekunci Diraja
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Sistem Fusion, dijelaskan
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Whispers of the Witch Tree - Cara Membuka Kunci Cangkuk Bergelut
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

Tutorial Java
1677
14
Tutorial PHP
1279
29
Tutorial C#
1257
24
Python vs JavaScript: Keluk Pembelajaran dan Kemudahan Penggunaan Python vs JavaScript: Keluk Pembelajaran dan Kemudahan Penggunaan Apr 16, 2025 am 12:12 AM

Python lebih sesuai untuk pemula, dengan lengkung pembelajaran yang lancar dan sintaks ringkas; JavaScript sesuai untuk pembangunan front-end, dengan lengkung pembelajaran yang curam dan sintaks yang fleksibel. 1. Sintaks Python adalah intuitif dan sesuai untuk sains data dan pembangunan back-end. 2. JavaScript adalah fleksibel dan digunakan secara meluas dalam pengaturcaraan depan dan pelayan.

JavaScript dan Web: Fungsi teras dan kes penggunaan JavaScript dan Web: Fungsi teras dan kes penggunaan Apr 18, 2025 am 12:19 AM

Penggunaan utama JavaScript dalam pembangunan web termasuk interaksi klien, pengesahan bentuk dan komunikasi tak segerak. 1) kemas kini kandungan dinamik dan interaksi pengguna melalui operasi DOM; 2) pengesahan pelanggan dijalankan sebelum pengguna mengemukakan data untuk meningkatkan pengalaman pengguna; 3) Komunikasi yang tidak bersesuaian dengan pelayan dicapai melalui teknologi Ajax.

JavaScript in Action: Contoh dan projek dunia nyata JavaScript in Action: Contoh dan projek dunia nyata Apr 19, 2025 am 12:13 AM

Aplikasi JavaScript di dunia nyata termasuk pembangunan depan dan back-end. 1) Memaparkan aplikasi front-end dengan membina aplikasi senarai TODO, yang melibatkan operasi DOM dan pemprosesan acara. 2) Membina Restfulapi melalui Node.js dan menyatakan untuk menunjukkan aplikasi back-end.

Memahami Enjin JavaScript: Butiran Pelaksanaan Memahami Enjin JavaScript: Butiran Pelaksanaan Apr 17, 2025 am 12:05 AM

Memahami bagaimana enjin JavaScript berfungsi secara dalaman adalah penting kepada pemaju kerana ia membantu menulis kod yang lebih cekap dan memahami kesesakan prestasi dan strategi pengoptimuman. 1) aliran kerja enjin termasuk tiga peringkat: parsing, penyusun dan pelaksanaan; 2) Semasa proses pelaksanaan, enjin akan melakukan pengoptimuman dinamik, seperti cache dalam talian dan kelas tersembunyi; 3) Amalan terbaik termasuk mengelakkan pembolehubah global, mengoptimumkan gelung, menggunakan const dan membiarkan, dan mengelakkan penggunaan penutupan yang berlebihan.

Python vs JavaScript: Persekitaran dan Alat Pembangunan Python vs JavaScript: Persekitaran dan Alat Pembangunan Apr 26, 2025 am 12:09 AM

Kedua -dua pilihan Python dan JavaScript dalam persekitaran pembangunan adalah penting. 1) Persekitaran pembangunan Python termasuk Pycharm, Jupyternotebook dan Anaconda, yang sesuai untuk sains data dan prototaip cepat. 2) Persekitaran pembangunan JavaScript termasuk node.js, vscode dan webpack, yang sesuai untuk pembangunan front-end dan back-end. Memilih alat yang betul mengikut keperluan projek dapat meningkatkan kecekapan pembangunan dan kadar kejayaan projek.

Peranan C/C dalam JavaScript Jurubah dan Penyusun Peranan C/C dalam JavaScript Jurubah dan Penyusun Apr 20, 2025 am 12:01 AM

C dan C memainkan peranan penting dalam enjin JavaScript, terutamanya digunakan untuk melaksanakan jurubahasa dan penyusun JIT. 1) C digunakan untuk menghuraikan kod sumber JavaScript dan menghasilkan pokok sintaks abstrak. 2) C bertanggungjawab untuk menjana dan melaksanakan bytecode. 3) C melaksanakan pengkompil JIT, mengoptimumkan dan menyusun kod hot-spot semasa runtime, dan dengan ketara meningkatkan kecekapan pelaksanaan JavaScript.

Python vs JavaScript: Gunakan Kes dan Aplikasi Membandingkan Python vs JavaScript: Gunakan Kes dan Aplikasi Membandingkan Apr 21, 2025 am 12:01 AM

Python lebih sesuai untuk sains data dan automasi, manakala JavaScript lebih sesuai untuk pembangunan front-end dan penuh. 1. Python berfungsi dengan baik dalam sains data dan pembelajaran mesin, menggunakan perpustakaan seperti numpy dan panda untuk pemprosesan data dan pemodelan. 2. Python adalah ringkas dan cekap dalam automasi dan skrip. 3. JavaScript sangat diperlukan dalam pembangunan front-end dan digunakan untuk membina laman web dinamik dan aplikasi satu halaman. 4. JavaScript memainkan peranan dalam pembangunan back-end melalui Node.js dan menyokong pembangunan stack penuh.

Dari laman web ke aplikasi: Aplikasi pelbagai JavaScript Dari laman web ke aplikasi: Aplikasi pelbagai JavaScript Apr 22, 2025 am 12:02 AM

JavaScript digunakan secara meluas di laman web, aplikasi mudah alih, aplikasi desktop dan pengaturcaraan sisi pelayan. 1) Dalam pembangunan laman web, JavaScript mengendalikan DOM bersama -sama dengan HTML dan CSS untuk mencapai kesan dinamik dan menyokong rangka kerja seperti JQuery dan React. 2) Melalui reaktnatif dan ionik, JavaScript digunakan untuk membangunkan aplikasi mudah alih rentas platform. 3) Rangka kerja elektron membolehkan JavaScript membina aplikasi desktop. 4) Node.js membolehkan JavaScript berjalan di sisi pelayan dan menyokong permintaan serentak yang tinggi.

See all articles