Skip to content

رندر سمت سرور (SSR)

نکته

رندر سمت سرور یا SSR، به قابلیتی در فریم‌ورک‌های فرانت‌اند مانند React ، Preact ، Vue و Svelte گفته می‌شود که اجازه می‌دهد همان اپلیکیشن در محیط Node.js اجرا شود، خروجی HTML تولید کند و سپس در مرورگر کاربر فرآیند "hydration" (فعال‌سازی تعاملی) را انجام دهد. اگر به دنبال یکپارچه‌سازی با فریم‌ورک‌های سنتی سمت سرور هستید، پیشنهاد می‌شود به راهنمای اتصال به بک‌اند مراجعه کنید.

این راهنما فرض را بر آن دارد که شما پیش‌تر با مفاهیم SSR در فریم‌ورک انتخابی‌تان آشنا هستید، و تمرکز آن صرفاً بر نکات خاص و کاربردی SSR در Vite است.

API سطح پایین

این رابط برنامه‌نویسی (API) مخصوص توسعه‌دهندگان کتابخانه‌ها و سازندگان فریم‌ورک‌هاست، و جزو ابزارهای سطح پایین Vite محسوب می‌شود. اگر هدف شما ساخت یک اپلیکیشن است، توصیه می‌کنیم ابتدا ابزارها و افزونه‌های سطح بالاتری که در بخش SSR پروژه Awesome Vite معرفی شده‌اند را بررسی کنید. با این حال، اپلیکیشن‌های بسیاری تاکنون با تکیه بر همین API بومی و سطح پایین Vite به‌خوبی توسعه یافته‌اند.

در حال حاضر، Vite در حال توسعه یک API پیشرفته‌تر برای SSR مبتنی بر Environment API است. برای آگاهی بیشتر می‌توانید به لینک مورد نظر مراجعه کنید.

پروژه‌های نمونه

Vite به‌صورت داخلی از رندر سمت سرور (SSR) پشتیبانی می‌کند. پروژه create-vite-extra مجموعه‌ای از تنظیمات آماده‌ی SSR را فراهم کرده که می‌توانید به‌عنوان مرجع در این راهنما از آن‌ها استفاده کنید:

همچنین می‌توانید این پروژه‌ها را به‌صورت محلی با اجرای دستور create-vite راه‌اندازی کنید. در بخش انتخاب فریم‌ورک، گزینه‌ی Others > create-vite-extra را انتخاب نمایید.

ساختار پروژه

یک اپلیکیشن معمولی با پشتیبانی از SSR (رندر سمت سرور) معمولاً دارای ساختاری مشابه زیر است:

- index.html
- server.js           # سرور اصلی اپلیکیشن
- src/
  - main.js           # کد عمومی برنامه که مستقل از محیط اجراست
  - entry-client.js   # در مرورگر DOM مسئول نصب برنامه روی
  - entry-server.js   # در فریم‌ورک SSR مربوط به API مسئول رندر کردن برنامه در سمت سرور با استفاده از

در فایل index.html باید به entry-client.js اشاره شود و همچنین محلی برای جای‌گذاری خروجی رندر شده توسط سرور در نظر گرفته شود:

index.html
html
<div id="app"><!--ssr-outlet--></div>
<script type="module" src="/src/entry-client.js"></script>

می‌توانید به‌جای ‎<!--ssr-outlet-->‎ از هر نشانه‌گذاری دلخواه دیگری استفاده کنید، فقط کافی است که قابل شناسایی و جایگزینی دقیق باشد.

منطق شرطی

اگه نیاز دارید کد شرطی بر اساس SSR یا کلاینت بنویسید، می‌تونید از این کد استفاده کنید:

js
if (import.meta.
env
.
SSR
) {
// ... فقط کد مخصوص سرور }

این شرط در زمان build به‌صورت استاتیک جایگزین می‌شود، بنابراین کدهایی که استفاده نمی‌شوند حذف می‌شوند (tree-shaking)، که باعث سبک‌تر شدن باندل نهایی می‌شود.

راه‌اندازی سرور توسعه (Dev Server)

وقتی دارید یک برنامه SSR می‌سازید، احتمالاً می‌خواهید کنترل کامل روی سرور اصلی‌تان داشته باشید و Vite را از محیط پروداکشن (production) جدا کنید. به همین دلیل، توصیه می‌شود از Vite در حالت middleware استفاده کنید. در ادامه یک نمونه با Express (نسخه ۴) آورده شده است:

server.js
js
import 
fs
from 'node:fs'
import
path
from 'node:path'
import {
fileURLToPath
} from 'node:url'
import
express
from 'express'
import {
createServer
as
createViteServer
} from 'vite'
const
__dirname
=
path
.
dirname
(
fileURLToPath
(import.meta.
url
))
async function
createServer
() {
const
app
=
express
()
// ایجاد کنید و نوع اپلیکیشن را به middleware را در حالت Vite سرور // غیرفعال شود و سرور والد Vite داخلی HTML تنظیم کنید تا سرو 'custom' // بتواند کنترل را به دست بگیرد const
vite
= await
createViteServer
({
server
: {
middlewareMode
: true },
appType
: 'custom'
}) // خودتان express استفاده کنید. اگر از روتر middleware به عنوان vite مربوط connect از نمونه // .استفاده کنید router.use استفاده می‌کنید، باید از (express.Router()) // زمانی که سرور مجدداً راه‌اندازی می‌شود )برای مثال بعد از اینکه کاربر فایل // همچنان همان ارجاع `vite.middlewares` (را تغییر می‌دهد، vite.config.js // .(های تزریق‌شده توسط پلاگین‌ها middleware‌ و Vite با یک پشته داخلی جدید از) خواهد بود // مورد زیر حتی پس از راه‌اندازی مجدد معتبر است
app
.
use
(
vite
.
middlewares
)
app
.
use
('*', async (
req
,
res
) => {
// را سرو می‌کنیم - در ادامه به این موضوع خواهیم پرداخت index.html فایل })
app
.
listen
(5173)
}
createServer
()

در اینجا vite یک نمونه از ViteDevServer است. vite.middlewares یک نمونه از Connect است که می‌توان از آن به عنوان یک middleware در هر فریم‌ورک Node.js که با connect سازگار است استفاده کرد.

گام بعدی پیاده‌سازی یک handler کلی (*) برای سرو HTML رندر شده در سمت سرور است:

server.js
js
app
.
use
('*', async (
req
,
res
,
next
) => {
const
url
=
req
.
originalUrl
try { // 1. index.html خواندن فایل let
template
=
fs
.
readFileSync
(
path
.
resolve
(
__dirname
, 'index.html'),
'utf-8', ) // 2. مربوط به HMR شامل تزریق کلاینت .Vite توسط HTML اعمال تغییرات // است Vite توسط پلاگین‌های HTML و همچنین انجام ترنسفورم‌های Vite // @vitejs/plugin-react های سراسری از preamble مثل
template
= await
vite
.
transformIndexHtml
(
url
,
template
)
// 3. را طوری تبدیل می‌کند ESM به‌طور خودکار کد ssrLoadModule .بارگذاری ورودی سمت سرور // مشابه، کارایی در HMR قابل اجرا باشد! نیازی به باندل نیست و با Node.js که در // کردن ماژول‌ها دارد invalidate const {
render
} = await
vite
.
ssrLoadModule
('/src/entry-server.js')
// 4. entry-server.js خروجی `render` اپلیکیشن. فرض شده که تابع HTML رندر کردن // فریم‌ورک استفاده می‌کند، SSR های API از // ReactDOMServer.renderToString() مثلاً const
appHtml
= await
render
(
url
)
// 5. HTML رندرشده در قالب HTML قرار دادن const
html
=
template
.
replace
(`<!--ssr-outlet-->`, () =>
appHtml
)
// 6. نهایی به مرورگر HTML ارسال
res
.
status
(200).
set
({ 'Content-Type': 'text/html' }).
end
(
html
)
} catch (
e
) {
// استک‌ترِیس را اصلاح می‌کند Vite ،در صورت بروز خطا، // تا به کد اصلی شما ارجاع دهد
vite
.
ssrFixStacktrace
(
e
)
next
(
e
)
} })

اسکریپت dev در فایل package.json نیز باید طوری تغییر داده شود که به‌جای آن از اسکریپت سرور استفاده کند.

package.json
diff
  "scripts": {
-   "dev": "vite"
+   "dev": "node server"
  }

ساخت(build) برای محیط پروداکشن

برای انتشار یک پروژه SSR در محیط پروداکشن، باید مراحل زیر را انجام دهید:

  1. یک بیلد (build) برای کلاینت مثل همیشه تولید شود؛
  2. یک بیلد SSR نیز تولید شود که مستقیماً با import()‎ بارگذاری شود، تا نیازی به استفاده از ssrLoadModule در Vite نباشد.

اسکریپت‌های ما در فایل package.json به شکل زیر خواهند بود:

package.json
json
{
  "scripts": {
    "dev": "node server",
    "build:client": "vite build --outDir dist/client",
    "build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
  }
}

به فلگ ‎--ssr توجه کنید که نشان می‌دهد این بیلد مخصوص SSR است. همچنین باید ورودی (entry) مربوط به SSR را مشخص کند.

سپس در فایل server.js باید منطق مخصوص پروداکشن را اضافه کنیم با بررسی مقدار process.env.NODE_ENV:

  • به جای خواندن فایل index.html از ریشه پروژه، از فایل dist/client/index.html به عنوان قالب استفاده کنید، چون این فایل شامل لینک‌های درست برای فایل‌های خروجی کلاینت است.

  • به جای استفاده از await vite.ssrLoadModule('/src/entry-server.js')، از import('./dist/server/entry-server.js') استفاده کنید (این فایل خروجی بیلد SSR است).

  • ساخت و استفاده از سرور توسعه vite را فقط در حالت توسعه (dev) انجام دهید و در حالت پروداکشن، از میان افزارهای سرو فایل‌های استاتیک برای ارائه فایل‌ها از مسیر dist/client استفاده کنید.

برای مشاهده نمونه پیاده‌سازی، به پروژه‌های نمونه مراجعه کنید.

تولید دستورهای پیش بارگذاری

دستور vite build از فلگ ‎--ssrManifest پشتیبانی می‌کند که یک فایل به نام .vite/ssr-manifest.json در دایرکتوری خروجی بیلد ایجاد می‌کند:

diff
- "build:client": "vite build --outDir dist/client",
+ "build:client": "vite build --outDir dist/client --ssrManifest",

اسکریپت بالا حالا فایل dist/client/.vite/ssr-manifest.json را برای بیلد کلاینت تولید می‌کند (بله، مانیفست SSR از بیلد کلاینت ایجاد می‌شود چون هدف این است که ID های ماژول‌ها را به فایل‌های مربوط به کلاینت متصل کنیم). این مانیفست شامل اطلاعاتی است که به ما می‌گوید هر ID ماژول به کدام بخش‌ها (chunks) و فایل‌های مرتبط با آن ماژول در بیلد کلاینت اشاره دارد.

برای استفاده از مانیفست، فریمورک‌ها باید روشی فراهم کنند تا ID ماژول‌های کامپوننت‌هایی که در هنگام رندر سرور استفاده شده‌اند، جمع‌آوری شوند.

‎@vitejs/plugin-vue این قابلیت را به طور پیش‌فرض ارائه می‌دهد و به طور خودکار ID های ماژول کامپوننت‌هایی که در هنگام رندر سرور استفاده شده‌اند را در SSR context مربوط به Vue ثبت می‌کند:

src/entry-server.js
js
const ctx = {}
const html = await vueServerRenderer.renderToString(app, ctx)
// ماژول‌هایی است که در طول رندر شدن استفاده شده‌اند ID یک مجموعه از ctx.modules حالا

در بخش پروداکشن فایل server.js، باید فایل manifest را بخوانیم و آن را به تابع render که از src/entry-server.js اکسپورت شده، بدهیم. این کار اطلاعات کافی برای تولید دستورهای preload مربوط به فایل‌هایی که در مسیرهای async استفاده شده‌اند را فراهم می‌کند. برای نمونه‌ی کامل، کد دمو را ببینید. همچنین می‌توان از این اطلاعات برای ارسال 103 Early Hints استفاده کرد.

پیش‌رندرینگ / تولید سایت ایستا (SSG)

اگر مسیرها (routes) و داده‌های مورد نیاز آن‌ها از قبل مشخص باشند، می‌توان آن‌ها را با استفاده از همان منطق SSR در حالت production، به صورت HTML ایستا (static) پیش‌پردازش کرد. این روش را می‌توان نوعی از تولید سایت ایستا یا SSG نیز در نظر گرفت. برای نمونه‌ی عملی، به اسکریپت pre-render دمو مراجعه کنید.

وابستگی‌های خارجی در SSR

در حالت SSR، وابستگی‌ها به‌صورت پیش‌فرض از سیستم تبدیل ماژول Vite خارج (externalize) می‌شوند. این کار باعث افزایش سرعت در هر دو زمان توسعه (dev) و ساخت (build) پروژه می‌شود.

اگر یک وابستگی نیاز داشته باشد که توسط زنجیره‌ی تبدیل Vite پردازش شود (مثلاً چون از قابلیت‌های Vite به‌صورت مستقیم و بدون تبدیل در آن استفاده شده)، می‌توانید آن را به گزینه‌ی ssr.noExternal اضافه کنید.

وابستگی‌هایی که لینک شده‌اند (مثل آن‌هایی که با npm link یا در محیط‌های monorepo استفاده شده‌اند)، به‌صورت پیش‌فرض external نمی‌شوند تا Vite بتواند از HMR استفاده کند. اگر نمی‌خواهید این رفتار اتفاق بیفتد (مثلاً برای شبیه‌سازی شرایطی که انگار وابستگی‌ها لینک نشده‌اند) می‌توانید آن‌ها را به گزینه‌ی ssr.external اضافه کنید.

کار با aliasها

اگر aliasهایی تعریف کرده‌اید که یک پکیج را به پکیج دیگری هدایت می‌کنند، ممکن است بهتر باشد به‌جای آن، خود پکیج‌های داخل node_modules را alias کنید تا قابلیت external شدن آن‌ها در SSR به درستی عمل کند. هر دو ابزار Yarn و pnpm از alias کردن با پیشوند npm:‎ پشتیبانی می‌کنند.

منطق افزونه مخصوص SSR

برخی فریم‌ورک‌ها مثل Vue یا Svelte، کامپوننت‌ها را بسته به این‌که برای کلاینت باشند یا SSR، به شکل‌های مختلفی کامپایل می‌کنند. با استفاده از این ویژگی، افزونه‌ها می‌توانند رفتار متفاوتی در حالت SSR نسبت به کلاینت داشته باشند. برای پشتیبانی از این تبدیل‌های شرطی، Vite یک ویژگی اضافی به نام ssr را در آبجکت options به هوک‌های زیر از افزونه‌ها منتقل می‌کند:

  • resolveId
  • load
  • transform

مثال:

js
export function 
mySSRPlugin
() {
return {
name
: 'my-ssr',
transform
(
code
,
id
,
options
) {
if (
options
?.
ssr
) {
// ... SSR انجام تبدیل مخصوص } }, } }

آبجکت options که در توابع load و transform استفاده می‌شود، اختیاری است. Rollup در حال حاضر از این آبجکت استفاده نمی‌کند، اما ممکن است در آینده برای افزودن اطلاعات بیشتر به این hookها از آن استفاده شود.

نکته

قبل از نسخه 2.7 از Vite، اطلاعات مربوط به SSR به جای اینکه در آبجکت options قرار بگیرد، به صورت یک پارامتر جداگانه به توابع پلاگین داده می‌شد. الان بیشتر فریم‌ورک‌ها و پلاگین‌ها به‌روز شده‌اند، ولی ممکن است هنوز در برخی منابع قدیمی نسخه قبلی این روش را ببینید.

هدف SSR

هدف پیش‌فرض برای ساخت SSR، محیط Node است، اما شما همچنین می‌توانید سرور را در یک Web Worker اجرا کنید. نحوه حل ارجاعات پکیج‌ها برای هر پلتفرم متفاوت است. شما می‌توانید هدف را به Web Worker تغییر دهید با تنظیم ssr.target به 'webworker'.

بسته‌بندی SSR

در برخی موارد مانند رانتایم webworker، ممکن است بخواهید بیلد SSR خود را به در یک فایل جاوا اسکریپت واحد بسته‌بندی (bundle) کنید. شما می‌توانید این رفتار را با تنظیم ssr.noExternal به true فعال کنید. این کار دو چیز انجام می‌دهد:

  • تمام وابستگی‌ها را به عنوان noExternal در نظر می‌گیرد.
  • اگر هرکدام از کتابخانه‌های داخلی Node.js ایمپورت شوند، یک خطا ایجاد می‌کند.

شرایط حل بسته برای SSR

به طور پیش‌فرض، برای ساخت SSR، Vite از شرایطی که در resolve.conditions تنظیم شده است، برای حل ورودی بسته‌ها استفاده می‌کند. شما می‌توانید با استفاده از گزینه‌های ssr.resolve.conditions و ssr.resolve.externalConditions، این رفتار را تغییر داده و سفارشی‌سازی کنید.

Vite CLI

دستورات CLI مانند ‎$ vite dev و ‎$ vite preview می‌توانند برای برنامه‌های SSR نیز استفاده شوند. شما می‌توانید میدل‌ورهای SSR خود را با استفاده از configureServer به سرور توسعه و با استفاده از configurePreviewServer به سرور پیش‌نمایش اضافه کنید.

نکته

از هوک پس از عملیات استفاده کنید تا میانه‌افزار SSR شما پس از میدل‌ورهای Vite اجرا شود.

تحت مجوز MIT منتشر شده. (dev)