Rumah >hujung hadapan web >tutorial js >Membina Aplikasi SSR React Sedia Pengeluaran
Dalam dunia di mana setiap milisaat penting, pemaparan bahagian pelayan telah menjadi keupayaan penting untuk aplikasi bahagian hadapan.
Panduan ini akan membimbing anda melalui corak asas untuk membina SSR sedia pengeluaran dengan React. Anda akan mendapat pemahaman tentang prinsip di sebalik rangka kerja berasaskan React dengan SSR terbina dalam (seperti Next.js) dan belajar cara mencipta penyelesaian tersuai anda sendiri.
Kod yang disediakan adalah sedia pengeluaran, menampilkan proses binaan lengkap untuk kedua-dua bahagian klien dan pelayan, termasuk Dockerfile. Dalam pelaksanaan ini, Vite digunakan untuk membina klien dan kod SSR, tetapi anda boleh menggunakan alat lain pilihan anda. Vite juga memberikan hot-reload semasa mod pembangunan untuk pelanggan.
Jika anda berminat dengan versi persediaan ini tanpa Vite, sila hubungi kami.
Perenderan sisi pelayan (SSR) ialah teknik dalam pembangunan web di mana pelayan menjana kandungan HTML halaman web sebelum menghantarnya ke penyemak imbas. Tidak seperti pemaparan sisi klien tradisional (CSR), di mana JavaScript membina kandungan pada peranti pengguna selepas memuatkan shell HTML kosong, SSR menyampaikan HTML yang dipaparkan sepenuhnya terus dari pelayan.
Faedah utama SSR:
Aliran apl anda dengan SSR mengikut langkah berikut:
Saya lebih suka menggunakan templat pnpm dan react-swc-ts Vite, tetapi anda boleh memilih sebarang persediaan lain.
pnpm create vite react-ssr-app --template react-swc-ts
Pasang kebergantungan:
pnpm create vite react-ssr-app --template react-swc-ts
Dalam aplikasi React biasa, terdapat satu titik masuk utama.tsx untuk index.html. Dengan SSR, anda memerlukan dua titik masuk: satu untuk pelayan dan satu untuk pelanggan.
Pelayan Node.js akan menjalankan apl anda dan menjana HTML dengan menjadikan komponen React anda kepada rentetan (renderToString).
pnpm install
Pelayar akan menghidratkan HTML yang dijana pelayan, menyambungkannya dengan JavaScript untuk menjadikan halaman itu interaktif.
Penghidratan ialah proses melampirkan pendengar acara dan gelagat dinamik lain pada HTML statik yang diberikan oleh pelayan.
// ./src/entry-server.tsx import { renderToString } from 'react-dom/server' import App from './App' export function render() { return renderToString(<App />) }
Kemas kini fail index.html dalam akar projek anda. pemegang tempat ialah tempat pelayan akan menyuntik HTML yang dijana.
// ./src/entry-client.tsx import { hydrateRoot } from 'react-dom/client' import { StrictMode } from 'react' import App from './App' import './index.css' hydrateRoot( document.getElementById('root')!, <StrictMode> <App /> </StrictMode>, )
Semua kebergantungan yang diperlukan untuk pelayan hendaklah dipasang sebagai kebergantungan pembangunan (devDependencies) untuk memastikan kebergantungan itu tidak disertakan dalam himpunan klien.
Seterusnya, buat folder dalam akar projek anda bernama ./server dan tambahkan fail berikut.
Eksport semula fail pelayan utama. Ini menjadikan perintah menjalankan lebih mudah.
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <div> <h3> Create Server </h3> <p>First, install the dependencies:<br> </p> <pre class="brush:php;toolbar:false">pnpm install -D express compression sirv tsup vite-node nodemon @types/express @types/compression
Pemalar HTML_KEY mesti sepadan dengan ulasan pemegang tempat dalam index.html. Pemalar lain mengurus tetapan persekitaran.
// ./server/index.ts export * from './app'
Sediakan pelayan Express dengan konfigurasi berbeza untuk pembangunan dan persekitaran pengeluaran.
// ./server/constants.ts export const NODE_ENV = process.env.NODE_ENV || 'development' export const APP_PORT = process.env.APP_PORT || 3000 export const PROD = NODE_ENV === 'production' export const HTML_KEY = `<!--app-html-->`
Dalam pembangunan, gunakan perisian tengah Vite untuk mengendalikan permintaan dan mengubah fail index.html secara dinamik dengan muat semula panas. Pelayan akan memuatkan aplikasi React dan menjadikannya HTML pada setiap permintaan.
// ./server/app.ts import express from 'express' import { PROD, APP_PORT } from './constants' import { setupProd } from './prod' import { setupDev } from './dev' export async function createServer() { const app = express() if (PROD) { await setupProd(app) } else { await setupDev(app) } app.listen(APP_PORT, () => { console.log(`http://localhost:${APP_PORT}`) }) } createServer()
Dalam pengeluaran, gunakan pemampatan untuk mengoptimumkan prestasi, sirv untuk menyampaikan fail statik dan himpunan pelayan pra-bina untuk memaparkan apl.
// ./server/dev.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import { HTML_KEY } from './constants' 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) { // Create a Vite development server in middleware mode const vite = await ( await import('vite') ).createServer({ root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', }) // Use Vite middleware for serving files app.use(vite.middlewares) app.get('*', async (req, res, next) => { try { // Read and transform the HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') html = await vite.transformIndexHtml(req.originalUrl, html) // Load the entry-server.tsx module and render the app const { render } = await vite.ssrLoadModule(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Fix stack traces for Vite and handle errors vite.ssrFixStacktrace(e as Error) console.error((e as Error).stack) next(e) } }) }
Untuk mengikuti amalan terbaik untuk membina aplikasi anda, anda harus mengecualikan semua pakej yang tidak diperlukan dan memasukkan hanya perkara yang sebenarnya digunakan oleh aplikasi anda.
Kemas kini konfigurasi Vite anda untuk mengoptimumkan proses binaan dan mengendalikan kebergantungan SSR:
// ./server/prod.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import compression from 'compression' import sirv from 'sirv' import { HTML_KEY } from './constants' 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) { // Use compression for responses app.use(compression()) // Serve static files from the client build folder app.use(sirv(CLIENT_PATH, { extensions: [] })) app.get('*', async (_, res, next) => { try { // Read the pre-built HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') // Import the server-side render function and generate HTML const { render } = await import(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Log errors and pass them to the error handler console.error((e as Error).stack) next(e) } }) }
Kemas kini tsconfig.json anda untuk memasukkan fail pelayan dan mengkonfigurasi TypeScript dengan sewajarnya:
pnpm create vite react-ssr-app --template react-swc-ts
Gunakan tsup, pengikat TypeScript, untuk membina kod pelayan. Pilihan noExternal menentukan pakej untuk digabungkan dengan pelayan. Pastikan anda memasukkan sebarang pakej tambahan yang digunakan oleh pelayan anda.
pnpm install
// ./src/entry-server.tsx import { renderToString } from 'react-dom/server' import App from './App' export function render() { return renderToString(<App />) }
Pembangunan: Gunakan arahan berikut untuk memulakan aplikasi dengan muat semula panas:
// ./src/entry-client.tsx import { hydrateRoot } from 'react-dom/client' import { StrictMode } from 'react' import App from './App' import './index.css' hydrateRoot( document.getElementById('root')!, <StrictMode> <App /> </StrictMode>, )
Pengeluaran: Bina aplikasi dan mulakan pelayan pengeluaran:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <div> <h3> Create Server </h3> <p>First, install the dependencies:<br> </p> <pre class="brush:php;toolbar:false">pnpm install -D express compression sirv tsup vite-node nodemon @types/express @types/compression
Untuk mengesahkan bahawa SSR berfungsi, semak permintaan rangkaian pertama ke pelayan anda. Respons harus mengandungi HTML yang diberikan sepenuhnya bagi aplikasi anda.
Untuk menambahkan halaman yang berbeza pada apl anda, anda perlu mengkonfigurasi penghalaan dengan betul dan mengendalikannya dalam kedua-dua titik masuk klien dan pelayan.
// ./server/index.ts export * from './app'
Balut aplikasi anda dengan BrowserRouter dalam titik masuk klien untuk mendayakan penghalaan sisi klien.
// ./server/constants.ts export const NODE_ENV = process.env.NODE_ENV || 'development' export const APP_PORT = process.env.APP_PORT || 3000 export const PROD = NODE_ENV === 'production' export const HTML_KEY = `<!--app-html-->`
Gunakan StaticRouter dalam titik masuk pelayan untuk mengendalikan penghalaan sebelah pelayan. Lulus url sebagai prop untuk memaparkan laluan yang betul berdasarkan permintaan.
// ./server/app.ts import express from 'express' import { PROD, APP_PORT } from './constants' import { setupProd } from './prod' import { setupDev } from './dev' export async function createServer() { const app = express() if (PROD) { await setupProd(app) } else { await setupDev(app) } app.listen(APP_PORT, () => { console.log(`http://localhost:${APP_PORT}`) }) } createServer()
Kemas kini kedua-dua persediaan pelayan pembangunan dan pengeluaran anda untuk menghantar URL permintaan kepada fungsi pemaparan:
// ./server/dev.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import { HTML_KEY } from './constants' 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) { // Create a Vite development server in middleware mode const vite = await ( await import('vite') ).createServer({ root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', }) // Use Vite middleware for serving files app.use(vite.middlewares) app.get('*', async (req, res, next) => { try { // Read and transform the HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') html = await vite.transformIndexHtml(req.originalUrl, html) // Load the entry-server.tsx module and render the app const { render } = await vite.ssrLoadModule(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Fix stack traces for Vite and handle errors vite.ssrFixStacktrace(e as Error) console.error((e as Error).stack) next(e) } }) }
Dengan perubahan ini, anda kini boleh membuat laluan dalam apl React anda yang serasi sepenuhnya dengan SSR. Walau bagaimanapun, pendekatan asas ini tidak mengendalikan komponen yang dimuatkan malas (React.lazy). Untuk menguruskan modul yang malas dimuatkan, sila rujuk artikel saya yang lain, Teknik SSR Reaksi Lanjutan dengan Penstriman dan Data Dinamik, dipautkan di bahagian bawah.
Berikut ialah Fail Docker untuk menyimpan aplikasi anda:
// ./server/prod.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import compression from 'compression' import sirv from 'sirv' import { HTML_KEY } from './constants' 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) { // Use compression for responses app.use(compression()) // Serve static files from the client build folder app.use(sirv(CLIENT_PATH, { extensions: [] })) app.get('*', async (_, res, next) => { try { // Read the pre-built HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') // Import the server-side render function and generate HTML const { render } = await import(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Log errors and pass them to the error handler console.error((e as Error).stack) next(e) } }) }
Membina dan Menjalankan Imej Docker
// ./vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' import { dependencies } from './package.json' export default defineConfig(({ mode }) => ({ plugins: [react()], ssr: { noExternal: mode === 'production' ? Object.keys(dependencies) : undefined, }, }))
{ "include": [ "src", "server", "vite.config.ts" ] }
Dalam panduan ini, kami telah mewujudkan asas yang kukuh untuk mencipta aplikasi SSR sedia pengeluaran dengan React. Anda telah mempelajari cara menyediakan projek, mengkonfigurasi penghalaan dan mencipta Fail Docker. Persediaan ini sesuai untuk membina halaman pendaratan atau apl kecil dengan cekap.
Ini adalah sebahagian daripada siri saya tentang SSR dengan React. Nantikan lebih banyak artikel!
Saya sentiasa terbuka untuk maklum balas, kerjasama atau membincangkan idea teknologi — jangan ragu untuk menghubungi kami!
Atas ialah kandungan terperinci Membina Aplikasi SSR React Sedia Pengeluaran. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!