برنامه نویسی

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 مراحل زیر را دنبال می کند:

  1. فایل HTML قالب را بخوانید.
  2. React را راه اندازی کنید و یک رشته HTML از محتوای برنامه ایجاد کنید.
  3. رشته HTML تولید شده را به قالب تزریق کنید.
  4. HTML کامل را به مرورگر ارسال کنید.
  5. در کلاینت، تگ های 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" />
    </span>Vite + React + TS<span class="nt"/>
  <span class="nt"/>
  <span class="nt"/>
    <span class="nt"><p> <span class="na">id=</span><span class="s">"root"</span><span class="nt">></span><span class="c"><!--app-html--></span><span class="nt"/></p></span>
    <span class="nt"><script><![CDATA[<span class="na">type=]]></script></span><span class="s">"module"</span> <span class="na">src=</span><span class="s">"/src/entry-client.tsx"</span><span class="nt">></span>
  <span class="nt"/>
<span class="nt"/>
</span></span></span></span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>وارد حالت تمام صفحه شوید
    

از حالت تمام صفحه خارج شوید

سرور ایجاد کنید

ابتدا وابستگی ها را نصب کنید:

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 (به زودی)

متصل بمانید

من همیشه آماده بازخورد، همکاری یا بحث در مورد ایده های فنی هستم – با خیال راحت تماس بگیرید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا