Building Production-Ready SSR React Applications

در دنیایی که هر میلی ثانیه اهمیت دارد، رندر سمت سرور به یک قابلیت ضروری برای برنامههای فرانت اند تبدیل شده است.
این راهنما شما را از طریق الگوهای اساسی برای ساختن یک SSR آماده تولید با React راهنمایی می کند. شما با SSR داخلی (مانند Next.js) اصولی را که در پشت چارچوبهای مبتنی بر React وجود دارد، درک خواهید کرد و یاد خواهید گرفت که چگونه راهحلهای سفارشی خود را ایجاد کنید.
کد ارائه شده آماده تولید است و شامل یک فرآیند ساخت کامل برای هر دو بخش مشتری و سرور، از جمله Dockerfile است. در این پیاده سازی از Vite برای ساخت کلاینت و کد SSR استفاده می شود، اما می توانید از هر ابزار دیگری که انتخاب می کنید استفاده کنید. Vite همچنین در طول حالت توسعه برای مشتری، بارگذاری مجدد داغ می دهد.
اگر به نسخهای از این راهاندازی بدون Vite علاقهمندید، با خیال راحت تماس بگیرید.
فهرست مطالب
SSR چیست؟
رندر سمت سرور (SSR) تکنیکی در توسعه وب است که در آن سرور محتوای HTML یک صفحه وب را قبل از ارسال آن به مرورگر تولید می کند. بر خلاف رندر سمت کلاینت سنتی (CSR)، که جاوا اسکریپت پس از بارگذاری یک پوسته خالی HTML، محتوا را در دستگاه کاربر ایجاد می کند، SSR HTML کاملاً رندر شده را مستقیماً از سرور تحویل می دهد.
مزایای کلیدی SSR:
- سئو بهبود یافته: از آنجایی که خزنده های موتورهای جستجو محتوای کاملاً رندر شده را دریافت می کنند، SSR نمایه سازی و رتبه بندی بهتر را تضمین می کند.
- سریعتر اولین رنگ: کاربران تقریباً بلافاصله محتوای معنیدار را مشاهده میکنند، زیرا سرور کارهای سنگین رندر را انجام میدهد.
- عملکرد پیشرفته: با کاهش بار کاری رندر در مرورگر، SSR تجربه روان تری را برای کاربران دستگاه های قدیمی یا کم قدرت فراهم می کند.
- انتقال یکپارچه داده از سرور به مشتری: SSR به شما امکان می دهد تا داده های سمت سرور پویا را بدون بازسازی بسته سرویس گیرنده به مشتری ارسال کنید.
ایجاد برنامه
جریان برنامه شما با SSR مراحل زیر را دنبال می کند:
- فایل HTML قالب را بخوانید.
- React را راه اندازی کنید و یک رشته HTML از محتوای برنامه ایجاد کنید.
- رشته HTML تولید شده را به قالب تزریق کنید.
- HTML کامل را به مرورگر ارسال کنید.
- در کلاینت، تگ های HTML را مطابقت دهید و برنامه را هیدراته کنید و آن را تعاملی کنید.
راه اندازی Vite
ترجیح میدم استفاده کنم pnpm
و react-swc-ts
قالب Vite، اما شما می توانید هر تنظیم دیگری را انتخاب کنید.
pnpm create vite react-ssr-app --template react-swc-ts
وابستگی ها را نصب کنید:
pnpm install
به روز رسانی React Components
در یک برنامه معمولی React، یک تک وجود دارد main.tsx
نقطه ورود برای index.html
. با SSR، شما به دو نقطه ورود نیاز دارید: یکی برای سرور و دیگری برای مشتری.
نقطه ورود به سرور
سرور Node.js برنامه شما را اجرا می کند و HTML را با رندر کردن اجزای React شما به یک رشته (renderToString) تولید می کند.
// ./src/entry-server.tsx
import { renderToString } from 'react-dom/server'
import App from './App'
export function render() {
return renderToString(<App />)
}
نقطه ورود مشتری
مرورگر HTML تولید شده توسط سرور را هیدراته می کند و آن را با جاوا اسکریپت متصل می کند تا صفحه را تعاملی کند.
هیدراتاسیون فرآیند پیوست کردن شنوندگان رویداد و سایر رفتارهای پویا به HTML ایستا ارائه شده توسط سرور است.
// ./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>,
)
به روز رسانی index.html
را به روز کنید index.html
در ریشه پروژه خود فایل کنید. را placeholder جایی است که سرور HTML تولید شده را تزریق می کند.
lang="en">
charset="UTF-8" />
rel="icon" type="image/svg+xml" href="/vite.svg" />
name="viewport" content="width=device-width, initial-scale=1.0" />
Vite + React + TS
id="root">
"module" src="/src/entry-client.tsx">
سرور ایجاد کنید
ابتدا وابستگی ها را نصب کنید:
pnpm install -D express compression sirv tsup vite-node nodemon @types/express @types/compression
تمام وابستگی های مورد نیاز برای سرور باید به عنوان وابستگی های توسعه نصب شوند (devDependencies
) برای اطمینان از اینکه آنها در بسته مشتری گنجانده نشده اند.
بعد، یک پوشه در ریشه پروژه خود با نام ایجاد کنید ./server
و فایل های زیر را اضافه کنید
صادرات مجدد فایل سرور اصلی
فایل سرور اصلی را دوباره صادر کنید. این امر اجرای دستورات را راحت تر می کند.
// ./server/index.ts
export * from './app'
تعریف ثابت ها
را HTML_KEY
ثابت باید با نظر نگهدارنده مکان مطابقت داشته باشد index.html
. سایر ثابت ها تنظیمات محیط را مدیریت می کنند.
// ./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 = ``
ایجاد سرور اکسپرس
یک سرور Express با تنظیمات مختلف برای محیط های توسعه و تولید راه اندازی کنید.
// ./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()
پیکربندی حالت توسعه
در توسعه، از میانافزار Vite برای رسیدگی به درخواستها و تبدیل پویا استفاده کنید index.html
فایل با بارگذاری مجدد داغ سرور برنامه React را بارگیری می کند و در هر درخواست آن را به HTML ارائه می کند.
// ./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)
}
})
}
پیکربندی حالت تولید
در تولید، استفاده کنید compression
برای بهینه سازی عملکرد، sirv
برای ارائه فایل های استاتیک و بسته سرور از پیش ساخته شده برای ارائه برنامه.
// ./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)
}
})
}
پیکربندی بیلد
برای پیروی از بهترین روشها برای ساخت برنامه خود، باید همه بستههای غیرضروری را حذف کنید و فقط مواردی را که برنامه شما واقعاً از آن استفاده میکند درج کنید.
در حال به روز رسانی پیکربندی Vite
پیکربندی Vite خود را برای بهینه سازی فرآیند ساخت و مدیریت وابستگی های SSR به روز کنید:
// ./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,
},
}))
به روز رسانی tsconfig.json
خود را به روز کنید tsconfig.json
برای گنجاندن فایل های سرور و پیکربندی مناسب TypeScript:
{
"include": [
"src",
"server",
"vite.config.ts"
]
}
ایجاد tsup
پیکربندی
استفاده کنید tsup
، یک باندلر TypeScript برای ساخت کد سرور. را noExternal
گزینه بسته ها را برای بسته شدن با سرور مشخص می کند. مطمئن شوید که بستههای اضافی را که سرورتان از آن استفاده میکند، اضافه کنید.
// ./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'],
})
اضافه کردن اسکریپت های ساخت
{
"scripts": {
"dev": "nodemon --exec vite-node server --watch server --ext ts",
"start": "NODE_ENV=production node dist/server/index.cjs",
"build": "tsc -b && npm run build:client && npm run build:ssr && npm run build:server",
"build:client": "vite build --outDir dist/client",
"build:ssr": "vite build --outDir dist/ssr --ssr src/entry-server.tsx",
"build:server": "tsup"
}
}
اجرای برنامه
توسعه: از دستور زیر برای شروع برنامه با بارگذاری مجدد داغ استفاده کنید:
pnpm dev
تولید: برنامه را بسازید و سرور تولید را راه اندازی کنید:
pnpm build && pnpm start
برای تأیید اینکه SSR کار می کند، اولین درخواست شبکه به سرور خود را بررسی کنید. پاسخ باید حاوی HTML کامل رندر شده برنامه شما باشد.
مسیریابی
برای افزودن صفحات مختلف به برنامه خود، باید مسیریابی را به درستی پیکربندی کنید و آن را در هر دو نقطه ورودی مشتری و سرور مدیریت کنید.
pnpm install react-router
افزودن مسیریابی سمت مشتری
برنامه خود را با BrowserRouter
در نقطه ورود مشتری برای فعال کردن مسیریابی سمت مشتری.
// ./src/entry-client.tsx
import { hydrateRoot } from 'react-dom/client'
import { StrictMode } from 'react'
import { BrowserRouter } from 'react-router'
import App from './App'
import './index.css'
hydrateRoot(
document.getElementById('root')!,
<StrictMode>
<BrowserRouter>
<App />
BrowserRouter>
StrictMode>,
)
اضافه کردن مسیریابی سمت سرور
استفاده کنید StaticRouter
در نقطه ورود سرور برای رسیدگی به مسیریابی سمت سرور. عبور کنید url
به عنوان پایه ای برای ارائه مسیر صحیح بر اساس درخواست.
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from './App'
export function render(url: string) {
return renderToString(
<StaticRouter location={url}>
<App />
StaticRouter>,
)
}
به روز رسانی تنظیمات سرور
تنظیمات سرور توسعه و تولید خود را به روز کنید تا URL درخواست را به سرور ارسال کنید render
تابع:
// ./server/dev.ts
// ./server/prod.ts
const appHtml = await render(req.url)
// Replace the placeholder with the rendered HTML
html = html.replace(HTML_KEY, appHtml)
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
//...
با این تغییرات، اکنون می توانید مسیرهایی را در برنامه React خود ایجاد کنید که کاملاً با SSR سازگار است. با این حال، این رویکرد اساسی اجزای بارگذاری شده تنبل را کنترل نمی کند (React.lazy
). برای مدیریت ماژول های بارگذاری شده تنبل، لطفاً به مقاله دیگر من مراجعه کنید. تکنیکهای پیشرفته React SSR با دادههای جریانی و دینامیکی، در پایین پیوند داده شده است.
داکر
در اینجا یک Dockerfile برای کانتینر کردن برنامه شما وجود دارد:
# Build App
FROM node:22-alpine as builder
WORKDIR /app
COPY . .
RUN corepack enable && pnpm install && pnpm build
# Production
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["npm", "run", "start"]
ساخت و اجرای Docker Image
docker build -t react-ssr-app .
docker run -p 3000:3000 react-ssr-app
نتیجه گیری
در این راهنما، ما یک پایه قوی برای ایجاد برنامه های کاربردی SSR آماده تولید با React ایجاد کرده ایم. شما یاد گرفته اید که چگونه پروژه را راه اندازی کنید، مسیریابی را پیکربندی کنید و یک Dockerfile ایجاد کنید. این تنظیمات برای ساخت صفحات فرود یا برنامه های کوچک به طور موثر ایده آل است.
کد را کاوش کنید
مقالات مرتبط
این بخشی از مجموعه من در SSR با React است. منتظر مقالات بیشتر باشید!
- Building Production-Ready SSR React Applications (شما اینجا هستید)
- تکنیکهای پیشرفته React SSR با دادههای جریانی و دینامیک (به زودی)
- راه اندازی تم ها در برنامه های SSR React (به زودی)
متصل بمانید
من همیشه آماده بازخورد، همکاری یا بحث در مورد ایده های فنی هستم – با خیال راحت تماس بگیرید!