Skip to content

رندرینگ سمت سرور | Server-Side Rendering (SSR)

مرور کلی

SSR چیست؟

Vue.js یک فریم‌ورک برای ساخت برنامه‌های سمت کلاینت است. به طور پیش‌فرض کامپوننت‌های Vue می‌توانند DOM را در مرورگر برای تولید خروجی دستکاری کنند. اما امکان رندر کردن همان کامپوننت‌ها به رشته‌های HTML در سمت سرور، ارسال مستقیم آن‌ها به مرورگر و در نهایت تبدیل آن به یک برنامه کاملا تعاملی در کلاینت نیز وجود دارد.

یک برنامه Vue.js رندر شده در سرور را می‌توان "isomorphic" یا "universal" در نظر گرفت، به این معنا که کد برنامه شما هم در سرور و هم در کلاینت اجرا می‌شود.

چرا SSR ؟

نسبت به یک برنامه تک‌صفحه‌ای (SPA) سمت کلاینت، مزایای اصلی SSR عبارتند از:

  • زمان رسیدن به محتوا سریعتر است: این مورد برای اینترنت ضعیف یا دستگاه‌های کند محسوس‌تر است. مارک‌آپ رندر شده در سرور نیاز ندارد تا تمام جاوااسکریپت دانلود و اجرا شود تا نمایش داده شود، بنابراین کاربر صفحه‌ی کامل رندر شده را سریع‌تر می‌بیند. علاوه بر این، درخواست دریافت داده در درخواست اول در سمت سرور انجام می‌شود که احتمالا اتصال سریع‌تری به پایگاه داده‌ی شما نسبت به کلاینت دارد. این به طور کلی منجر به بهبود شاخص‌های Core Web Vitals، تجربه کاربری بهتر و حیاتی بودن برای برنامه‌هایی که زمان محتوا مستقیما با نرخ تبدیل ارتباط دارد، می‌شود. (مترجم: منظور از نرخ تبدیل درصد کاربرانی که عمل مورد نظر ما را انجام می‌دهند، هست. مثل خرید کالا یا ثبت نام در سایت)

  • الگوی ذهنی یکپارچه‌تر: شما از یک زبان و الگوی ذهنی اعلانی و مبتنی بر کامپوننت برای توسعه‌ی کل برنامه استفاده می‌کنید، به جای پرش رفت و برگشت مداوم بین تمپلیت سیستم بک‌اند و فریم‌ورک فرانت‌اند.

  • SEO بهتر: خزنده‌های موتورهای جستجو صفحه‌ی کامل رندر شده را به طور مستقیم می‌بینند.

    نکته

    در حال حاضر، گوگل و بینگ به خوبی برنامه‌های جاوااسکریپت synchronous را ایندکس می‌کنند. کلمه کلیدی اینجا synchronous است. اگر برنامه شما با یک اسپینر بارگذاری شروع شود، سپس محتوا را از طریق Ajax بارگیری کند، خزنده منتظر شما نخواهد ماند تا کارتان تمام شود. این بدان معناست که اگر محتوایی را به صورت غیرهمگام در صفحاتی بارگیری می‌کنید که SEO برای آن‌ها مهم است، ممکن است نیاز به SSR داشته باشید. (مترجم: سایت را در pagespeed تست کنید.)

همچنین برخی مبادلات (trade-offs) برای استفاده از SSR وجود دارد:

  • محدودیت‌های توسعه: کدهای مخصوص سمت مرورگر فقط می‌توانند در برخی از هوک‌های چرخه حیات استفاده شوند. برخی کتابخانه‌های خارجی ممکن است نیاز به تنظیمات خاصی برای اجرا در یک برنامه رندر شده در سرور داشته باشند.

  • نیازمندی‌های پیکربندی و استقرار پیچیده‌تر: برخلاف یک SPA کاملا استاتیک که می‌تواند روی هر سرور فایل استاتیک مستقر شود، یک برنامه رندر شده در سرور نیاز به محیطی دارد که یک سرور Node.js در آن بتواند اجرا شود.

  • بار سمت سرور بیشتر: رندر کردن یک برنامه کامل در Node.js پرهزینه‌تر از صرفا سرو کردن فایل‌های استاتیک است، بنابراین اگر ترافیک زیادی را انتظار دارید، برای بار متناظر سرور آماده باشید و استراتژی‌های کش کردن داده را به کار ببندید.

قبل از استفاده از SSR برای برنامه خود، اولین سوالی که باید بپرسید این است که آیا واقعا به آن نیاز دارید یا خیر. این موضوع عمدتا بستگی به اهمیت زمان بارگذاری محتوا برای برنامه شما دارد. به عنوان مثال، اگر در حال ساخت یک داشبورد داخلی هستید که چند صد میلی‌ثانیه تاخیر در بارگذاری اولیه برای آن مهم نیست، SSR می‌تواند بیش از حد باشد. با این حال، در مواردی که زمان بارگذاری محتوا کاملا حیاتی است، SSR می‌تواند به شما کمک کند تا بهترین عملکرد بارگذاری اولیه را به دست آورید.

SSR در برابر SSG

Static Site Generation (SSG) یا پیش‌رندرینگ، تکنیک محبوب دیگری برای ساخت وب‌سایت‌های سریع است. اگر داده‌های مورد نیاز برای رندر کردن صفحه برای همه کاربران یکسان باشد، به جای رندر مکرر، می‌توان آن را یک‌بار در طول فرایند ساخت رندر کرد. صفحات پیش‌رندر شده به عنوان فایل‌های HTML استاتیک تولید و سرو می‌شوند.

SSG ویژگی‌های عملکردی مشابهی با برنامه‌های SSR دارد: عملکرد زمان بارگذاری محتوا عالی است. در عین حال، ارزان‌تر و ساده‌تر از برنامه‌های SSR برای استقرار است زیرا خروجی HTML و asset ها استاتیک هستن. کلمه کلیدی اینجا استاتیک است: SSG فقط می‌تواند برای صفحاتی که از داده‌های استاتیک استفاده می‌کنند به کار رود، یعنی داده‌هایی که در زمان ساخت شناخته شده‌اند و بین استقرارها تغییر نمی‌کنند. هر بار که داده‌ها تغییر کنند، نیاز به استقرار جدید است.

اگر فقط به دنبال بهبود SEO چند صفحه بازاریابی (مثلا /، ‎/about، ‎/contact و غیره) هستید، احتمالا SSG به جای SSR مناسب‌تر است. SSG همچنین برای وب‌سایت‌های محتوا-محور مثل سایت‌های مستندات یا وبلاگ‌ها عالی است. در واقع، وب‌سایتی که در حال مطالعه آن هستید، به صورت استاتیک با استفاده از VitePress که یک ژنراتور سایت استاتیک Vue‌محور است، تولید شده است.

آموزش پایه

رندر کردن یک برنامه

اجازه بدهید یک نمونه بسیار ساده از SSR را در Vue بررسی کنیم:

  1. یک دایرکتوری جدید ایجاد کرده و وارد آن شوید
  2. npm init -y را اجرا کنید
  3. "type": "module" را در package.json اضافه کنید تا Node.js در حالت ES modules mode اجرا شود.
  4. npm install vue را اجرا کنید
  5. فایل example.js را ایجاد کنید:
js
// بر روی سرور اجرا می‌شود Node.js این در
import { createSSRApp } from 'vue'
// در دسترس است vue/server-renderer تحت Vue رندرینگ سمت سرور API
import { renderToString } from 'vue/server-renderer'

const app = createSSRApp({
  data: () => ({ count: 1 }),
  template: `<button @click="count++">{{ count }}</button>`
})

renderToString(app).then((html) => {
  console.log(html)
})

سپس آن را اجرا کنید:

sh
> node example.js

باید خروجی زیر را در خط فرمان ببینید:

<button>1</button>

renderToString()‎ یک نمونه از برنامه Vue دریافت می‌کند و یک Promise برمی‌گرداند که HTML رندر شده برنامه در آن قرار دارد. همچنین امکان رندرینگ پویا با استفاده از Node.js Stream API یا Web Streams API وجود دارد. جزئیات کامل را در مرجع SSR API Reference ببینید.

سپس می‌توانیم کد SSR برنامه Vue را در یک هندلر درخواست سرور (server request handler) قرار دهیم که مارک‌آپ برنامه را در HTML صفحه کامل قرار می‌دهد. از express برای مراحل بعدی استفاده خواهیم کرد:

-npm install express را اجرا کنید

  • فایل server.js زیر را ایجاد کنید:
js
import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'

const server = express()

server.get('/', (req, res) => {
  const app = createSSRApp({
    data: () => ({ count: 1 }),
    template: `<button @click="count++">{{ count }}</button>`
  })

  renderToString(app).then((html) => {
    res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>Vue SSR Example</title>
      </head>
      <body>
        <div id="app">${html}</div>
      </body>
    </html>
    `)
  })
})

server.listen(3000, () => {
  console.log('ready')
})

در نهایت، node server.js را اجرا کرده و به http://localhost:3000 مراجعه کنید. باید صفحه را با دکمه ای که کار می‌کند ببینید. (مترجم: ادامه را بخوانید)

آن را در StackBlitz امتحان کنید

هایدریشن کردن کلاینت | Client Hydration

اگر روی دکمه کلیک کنید، متوجه می‌شوید عدد تغییر نمی‌کند. HTML در کلاینت کاملاً استاتیک است زیرا ما Vue را در مرورگر بارگذاری نکرده‌ایم.

برای تعاملی کردن برنامه سمت کلاینت، Vue باید مرحله هایدریشن (Hydration) را انجام دهد. در طول هایدریشن، Vue همان برنامه‌ای را که در سرور اجرا شده ایجاد می‌کند، هر کامپوننت را به گره‌های DOM متناظرش متصل می‌کند و رویدادهای DOM را ضمیمه می‌کند. (مترجم: Hydration کاری هست که توی مرورگر انجام میشه تا صفحه ای که سمت سرور رندر شده رو به وضعیتی برسونه که انگار توی کلاینت رندر شده. برای درک بیشتر این مقاله رو بخونید.)

برای mount کردن برنامه در حالت هایدریشن، باید از createSSRApp()‎ به جای createApp()‎ استفاده کنیم:

js
// این در مرورگر اجرا می‌شود
import { createSSRApp } from 'vue'

const app = createSSRApp({
  // ...همان برنامه سرور
})

// در کلاینت به این معنی است SSR کردن یک برنامه mount
// از قبل رندر شده و هایدریشن به جای HTML که
// جدید انجام می‌شود DOM نودهای mount
app.mount('#app')

ساختار کد

توجه کنید که چطور از همان پیاده‌سازی برنامه، در سرور استفاده کنیم. اینجاست که باید در مورد ساختار کد در یک برنامه SSR فکر کنیم - چگونه می‌توان کد برنامه را بین سرور و کلاینت به اشتراک گذاشت؟

اینجا ساده‌ترین پیکربندی را نشان می‌دهیم. ابتدا منطق ایجاد برنامه را در یک فایل جداگانه، app.js، تفکیک می‌کنیم:

js
// app.js (بین سرور و کلاینت به اشتراک گذاشته می‌شود)
import { createSSRApp } from 'vue'

export function createApp() {
  return createSSRApp({
    data: () => ({ count: 1 }),
    template: `<button @click="count++">{{ count }}</button>`
  })
}

این فایل و وابستگی‌هایش بین سرور و کلاینت به اشتراک گذاشته می‌شوند - آن‌ها را کد یونیورسال می‌نامیم. چند نکته وجود دارد که هنگام نوشتن کد یونیورسال باید به آن‌ها توجه کنید که در ادامه بحث می‌کنیم.

کلاینت کد یونیورسال را import می‌کند، برنامه را ایجاد می‌کند و mount می‌کند:

js
// client.js
import { createApp } from './app.js'

createApp().mount('#app')

و سرور از همان منطق ایجاد برنامه در هندلر درخواست استفاده می‌کند:

js
// server.js (irrelevant code omitted)
import { createApp } from './app.js'

server.get('/', (req, res) => {
  const app = createApp()
  renderToString(app).then(html => {
    // ...
  })
})

علاوه بر این، برای بارگذاری فایل‌های کلاینت در مرورگر، نیاز است تا:

  1. فایل‌های کلاینت را با اضافه کردن server.use(express.static('.')) در server.js سرو کنیم.
  2. کلاینت را با اضافه کردن <script type="module" src="/client.js"></script> به تمپلیت HTML بارگذاری کنیم.
  3. مواردی مانند import * from 'vue'‎ را در مرورگر با اضافه کردن یک Import Map به HTML پشتیبانی می‌کنیم.

نمونه کامل را در StackBlitz امتحان کنید. دکمه اکنون قابل تعامل با کاربر است!

راه حل‌های سطوح بالاتر

انتقال از مثال به یک برنامه SSR آماده بهره‌برداری، نیازمند کارهای بسیار بیشتری است. ما نیاز خواهیم داشت تا:

  • از SFCهای Vue و سایر نیازمندی‌های مراحل بیلد پشتیبانی کنیم. در واقع، نیاز به هماهنگی دو بیلد برای یک برنامه خواهیم داشت: یکی برای کلاینت و یکی برای سرور.

    نکته

    کامپوننت‌های Vue برای SSR به طور متفاوت کامپایل می‌شوند - تمپلیت‌ها به جای توابع رندر کننده DOM مجازی به رشته‌های ترکیب شده تبدیل می‌شوند برای بهینه‌سازی رندرینگ.

  • در هندلر درخواست سرور، HTML را با لینک‌های assetهای کلاینت رندر کنیم. همچنین ممکن است نیاز به تعویض بین حالت‌های SSR و SSG داشته باشیم و یا حتی هر دو را در یک برنامه ترکیب کنیم.

  • مدیریت route ها، درخواست دریافت داده و state management store ها به صورت یونیورسال.

یک پیاده‌سازی کامل بسیار پیچیده خواهد بود و به مجموعه ابزار بیلد انتخاب شده بستگی دارد. بنابراین، توصیه می‌کنیم از یک راه حل سطح بالاتر و جهت‌دار استفاده کنید که پیچیدگی‌ها را برای شما مخفی می‌کند. در زیر چند راه حل SSR توصیه شده در اکوسیستم Vue را معرفی می‌کنیم.

Nuxt

Nuxt یک فریم‌ورک سطح بالاتر است که بر روی اکوسیستم Vue ساخته شده و تجربه توسعه یکپارچه‌ای برای نوشتن برنامه‌های یونیورسال Vue فراهم می‌کند. بهتر از آن، می‌توانید از آن به عنوان یک ژنراتور سایت استاتیک هم استفاده کنید! توصیه می‌کنیم حتما آن را امتحان کنید.

Quasar

Quasar یک راه حل کامل مبتنی بر Vue است که امکان هدف‌گیری SPA ، SSR ، PWA ، برنامه موبایل، برنامه دسکتاپ و افزونه مرورگر را با استفاده از یک کدبیس واحد فراهم می‌کند. این فریم‌ورک نه تنها مراحل بیلد را مدیریت می‌کند، بلکه مجموعه کاملی از کامپوننت‌های رابط کاربری سازگار با Material Design را نیز ارائه می‌دهد.

Vite SSR

Vite از پشتیبانی درونی برای رندرینگ سرور ساید Vue برخوردار است، اما عمداً low-level است. اگر می‌خواهید مستقیماً از Vite استفاده کنید، vite-plugin-ssr را که یک افزونه کامیونیتی است و جزئیات چالش‌برانگیز بسیاری را برای شما مخفی می‌کند، بررسی کنید.

همچنین می‌توانید یک پروژه نمونه Vue + Vite SSR با پیکربندی دستی را اینجا پیدا کنید که می‌تواند به عنوان پایه‌ای برای ساختن روی آن باشد. توجه داشته باشید این فقط در صورتی توصیه می‌شود که با SSR / ابزارهای بیلد تجربه داشته باشید و واقعاً کنترل کامل روی معماری سطح بالاتر می‌خواهید.

نوشتن کد سازگار با SSR

صرف‌نظر از انتخاب پیکربندی بیلد یا فریم‌ورک سطح بالاتر، اصولی وجود دارد که در تمام برنامه‌های SSR در Vue اعمال می‌شود.

واکنش‌پذیری در سرور | Reactivity on the Server

در طول SSR، هر URL درخواست به یک state دلخواه از برنامه ما نگاشت می‌شود. تعامل کاربری وجود ندارد و DOM به‌روز نمی‌شود، بنابراین واکنش‌پذیری (reactivity) در سرور غیرضروری است. به طور پیش‌فرض، واکنش‌پذیری در طول SSR برای عملکرد بهتر غیرفعال است.

هوک‌های چرخه حیات کامپوننت

از آنجا که به‌روزرسانی‌های پویا وجود ندارد، هوک‌های چرخه حیات مانند mountedonMounted یا updatedonUpdated در طول SSR اجرا نمی‌شوند و فقط در کلاینت اجرا می‌شوند. تنها هوک‌هایی که در طول SSR فراخوانی می‌شوند beforeCreate و created هستند.

باید از کدی که اثرات جانبی تولید می‌کند و نیاز به پاکسازی در beforeCreate و createdsetup()‎ یا اسکوپ ریشه <script setup> دارد، اجتناب کرد. مثالی از چنین اثرات جانبی تنظیم تایمرها با setInterval است. در کد سمت کلاینت ممکن است یک تایمر تنظیم کنیم و سپس آن را درbeforeUnmountonBeforeUnmount یا unmountedonUnmountedحذف کنیم. اما چون هوک‌های unmount در SSR هرگز صدا زده نمی‌شوند، تایمرها برای همیشه باقی خواهند ماند. برای اجتناب از این موضوع، کد اثرات جانبی دار را به mountedonMounted منتقل کنید.

دسترسی به APIهای ویژه پلتفرم

کد یونیورسال نمی‌تواند دسترسی به APIهای ویژه پلتفرم را فرض کند، بنابراین اگر کد شما به طور مستقیم از گلوبال‌های ویژه مرورگر مانند window یا document استفاده کند، در Node.js خطا خواهند داد و برعکس.

برای کارهایی که بین سرور و کلاینت به اشتراک گذاشته می‌شوند اما APIهای متفاوتی دارند، توصیه می‌شود پیاده‌سازی‌های ویژه پلتفرم را در یک API یونیورسال کپسوله کنید یا از کتابخانه‌هایی استفاده کنید که این کار را برای شما انجام می‌دهند. برای مثال، می‌توانید از node-fetch برای استفاده از همان API fetch در هر دو سرور و کلاینت استفاده کنید.

برای APIهای ویژه مرورگر، رویکرد متداول دسترسی lazily در درون هوک‌های چرخه حیات سمت کلاینت مانند mountedonMounted.

توجه داشته باشید اگر یک کتابخانه شخص‌ثالث با هدف استفاده یونیورسال نوشته نشده باشد، ادغام آن در یک برنامه رندر شده در سرور ممکن است دشوار باشد. شاید شما بتوانید با شبیه‌سازی برخی از گلوبال‌ها با آن کار کنید، اما کثیف و احتمال تداخل با کد تشخیص environment کتابخانه‌های دیگر وجود دارد.

آلودگی State بین درخواست‌ها - Cross-Request State Pollution

در فصل مدیریت state، ما الگوی ساده مدیریت state با استفاده از APIهای reactivity را معرفی کردیم. در بستر SSR، این الگو نیاز به تنظیمات اضافی دارد.

این الگو state مشترکی را در اسکوپ ریشه ماژول جاوااسکریپت اعلان می‌کند. این آن‌ها را singletons می‌کند - یعنی تنها یک نمونه از آبجکت reactive در تمام چرخه حیات برنامه وجود دارد. این در یک برنامه Vue خالص سمت کلاینت، همان‌طور که انتظار می‌رود عمل می‌کند، زیرا ماژول‌های برنامه ما برای هر بار مراجعه صفحه مرورگر از ابتدا مجددا مقداردهی اولیه می‌شوند.

اما در بستر SSR، ماژول‌های برنامه معمولاً تنها یک بار هنگام راه‌اندازی سرور مقداردهی اولیه می‌شوند. همان نمونه‌های ماژول در درخواست‌های متعدد سرور استفاده مجدد می‌شوند، و بنابراین آبجکت‌های state سینگلتون ما نیز در درخواست‌های متعدد سرور استفاده مجدد می‌شوند. اگر state سینگلتون مشترک را با داده‌های مهم یک کاربر تغییر دهیم، به طور تصادفی ممکن است در درخواستی از کاربر دیگر نشت کند. به این آلودگی state بین درخواست‌ها می‌گوییم.

از لحاظ فنی می‌توانیم تمام ماژول‌های جاوااسکریپ را در هر درخواست، همانند مرورگرها، مجددا مقداردهی اولیه کنیم. اما مقداردهی اولیه ماژول‌های جاوااسکریپ هزینه‌بر است و تاثیر قابل توجهی روی عملکرد سرور خواهد داشت.

راه حل توصیه شده ایجاد یک نمونه جدید از تمام برنامه - شامل روتر و store های گلوبال - در هر درخواست است. سپس، به جای وارد کردن مستقیم آن در کامپوننت‌ها، state اشتراکی را با استفاده از provide در سطح برنامه ارائه دهید و آن را در کامپوننت‌هایی که نیاز دارند inject کنید:

js
// app.js (بین سرور و کلاینت به اشتراک گذاشته شده)
import { createSSRApp } from 'vue'
import { createStore } from './store.js'

// در هر درخواست صدا زده می‌شود
export function createApp() {
  const app = createSSRApp(/* ... */)
  // در هر درخواست store ایجاد نمونه جدید از
  const store = createStore(/* ... */)
  // در سطح برنامه store ارائه
  app.provide('store', store)
  // را برای اهداف هایدریشن قرار دهید store همچنین
  return { app, store }
}

در کتابخانه‌های مدیریت وضعیت مانند Pinia این موضوع را در نظر گرفته شده‌اند. Pinia's SSR guide را برای جزئیات بیشتر ببینید.

عدم تطابق هایدریشن

اگر ساختار DOM در HTML پیش‌رِندر شده با خروجی مورد انتظار برنامه سمت کلاینت مطابقت نداشته باشد، خطای عدم تطابق هایدریشن رخ می‌دهد. عدم تطابق هایدریشن معمولاً توسط علل زیر پیش می‌آید:

  1. تمپلیت حاوی ساختار تو در توی HTML نامعتبر است، و HTML رندر شده توسط رفتار پارسر HTML مرورگر "اصلاح" شده است. برای مثال، یک مشکل رایج این است که <div> نمی‌تواند درون <p> قرار بگیرد:

    html
    <p><div>hi</div></p>

    اگر این HTML را در سرور رندر کنیم، وقتی مرورگر با اولین <div> بعد از <p> مواجه شود آن را متوقف می‌کند و آن را به ساختار DOM زیر تجزیه می‌کند:

    html
    <p></p>
    <div>hi</div>
    <p></p>
  2. داده‌های مورد استفاده در طول رندر حاوی مقادیر تولید شده تصادفی است. از آنجا که همان برنامه دو بار اجرا می‌شود - یک بار در سرور و یک بار در کلاینت - مقادیر تصادفی تضمین نشده‌اند که بین دو اجرا یکسان باشند. دو راه برای اجتناب از عدم تطابق القا شده توسط مقادیر تصادفی وجود دارد:

    1. از v-if + onMounted برای رندر قسمت وابسته به مقادیر تصادفی فقط در کلاینت استفاده کنید. فریم‌ورک شما همچنین ممکن است ویژگی‌های درون‌ساخته‌ای برای ساده کردن این کار داشته باشد، برای مثال کامپوننت <ClientOnly> در VitePress.

    2. از یک کتابخانه ژنراتور اعداد تصادفی که از تولید با بذر پشتیبانی می‌کند استفاده کنید و تضمین کنید که اجرای سرور و اجرای کلاینت از همان بذر استفاده می‌کنند (مثلا با قرار دادن بذر در وضعیت سریالیزه شده و بازیابی آن در کلاینت).

  3. سرور و کلاینت در مناطق زمانی متفاوت قرار دارند. گاهی اوقات ممکن است بخواهیم یک تایم‌استمپ را به زمان محلی کاربر تبدیل کنیم. با این حال، منطقه زمانی در طول اجرای سرور و منطقه زمانی در طول اجرای کلاینت همیشه یکسان نیستند، و ممکن است در طول اجرای سرور به طور قابل اطمینان منطقه زمانی کاربر را ندانیم. در چنین مواردی، تبدیل زمان محلی نیز باید به عنوان یک عملیات سمت کلاینت انجام شود.

وقتی Vue با عدم تطابق هایدریشن مواجه می‌شود، سعی می‌کند به طور خودکار بازیابی کرده و DOM پیش‌رندر شده را تنظیم کند تا با وضعیت سمت کلاینت مطابقت داشته باشد. این منجر به افت عملکرد رندرینگ به دلیل دور انداختن نودهای نادرست و mount نودهای جدید می‌شود، اما در اکثر موارد، برنامه باید همان‌طور که انتظار می‌رود ادامه دهد. با این حال، بهتر است عدم تطابق‌های هایدریشن در طول توسعه حذف شوند.

دایرکتیوهای سفارشی

از آنجا که اکثر دایرکتیوهای سفارشی شامل دستکاری مستقیم DOM هستند، در طول SSR نادیده گرفته می‌شوند. با این حال، اگر می‌خواهید مشخص کنید که یک دایرکتیو سفارشی چگونه باید رندر شود (یعنی چه صفاتی را باید به عنصر رندر شده اضافه کند)، می‌توانید از هوک دایرکتیو getSSRProps استفاده کنید:

js
const myDirective = {
  mounted(el, binding) {
    // پیاده‌سازی سمت کلاینت:
    // DOM به‌روزرسانی مستقیم
    el.id = binding.value
  },
  getSSRProps(binding) {
    // پیاده‌سازی سمت سرور:
    // صفاتی که باید رندر شوند را برمی‌گرداند
    // را دریافت می‌کند binding فقط دایرکتیو getSSRProps
    return {
      id: binding.value
    }
  }
}

تله‌پورت‌ها

تله‌پورت‌ها نیاز به مدیریت ویژه در طول SSR دارند. اگر برنامه رندر شده حاوی تله‌پورت‌ها باشد، محتوای تله‌پورت شده بخشی از رشته رندر شده نخواهد بود. راه حل ساده‌تر این است که تله‌پورت را به صورت شرطی در mount رندر کنیم.

اگر نیاز به هایدریشن کردن محتوای تله‌پورت شده دارید، آن‌ها تحت خاصیت teleports آبجکت بستر SSR در دسترس هستند:

js
const ctx = {}
const html = await renderToString(app, ctx)

console.log(ctx.teleports) // { '#teleported': 'teleported content' }

شما نیاز دارید مارک‌آپ تله‌پورت را در مکان درست در HTML صفحه نهایی مشابه نحوه‌ای که مارک‌آپ برنامه اصلی را تزریق می‌کنید، تزریق کنید.

نکته

هنگام استفاده از تله‌پورت و SSR با هم از انتخاب body اجتناب کنید - معمولاً <body> حاوی سایر محتوای رندر شده در سرور است که امکان تشخیص مکان شروع درست برای هایدریشن را برای تله‌پورت‌ها غیرممکن می‌کند.

به جای آن، یک کانتینر اختصاصی را ترجیح دهید، مثلا <div id="teleported"></div> که فقط حاوی محتوای تله‌پورت شده است.

رندرینگ سمت سرور | Server-Side Rendering (SSR) has loaded