Skip to content

التصيير من جانب الخادوم (SSR)

نظرة عامة

ماهو التصيير من جانب الخادوم؟

Vue.js هي إطار عمل لبناء تطبيقات من جانب المستخدم. افتراضيًا، مكوّنات Vue تنتج الـDOM وتعمل عليه في المتصفح كناتج. ومع ذلك، من الممكن أيضًا تصيير نفس المكوّنات إلى سلاسل نصية HTML على الخادوم، ثم إرسالها مباشرة إلى المتصفح، وأخيرًا "إنعاش" الـHTML الساكن بالبيانات التفاعلية وتحويله إلى تطبيق تفاعلي بالكامل على جهة المستخدم.

يعتبر تطبيق Vue.js مصيّر من جانب الخادوم أيضًا "مُتَمَاثِل" أو "عالمي"، حيث تعمل معظم الشيفرة للتطبيق على الخادوم و على المستخدم.

لماذا التصيير من جانب الخادوم؟

بالمقارنة مع تطبيق أحادي الصفحة من جانب المستخدم (SPA)، فإن ميزة التصيير من جانب الخادوم تكمن أساسا في:

  • أسرع زمن للوصول إلى المحتوى: هذا أكثر وضوحًا على الإنترنت البطيء أو الأجهزة البطيئة. لا يحتاج الـHTML المصيّر من جانب الخادوم إلى الانتظار حتى تنزل وتنفذ شيفرة الـJavascript كامل, ليُعرض، لذا سيرى المستخدم صفحة مصيّرة بالكامل بشكل أسرع. بالإضافة إلى ذلك، تجلب البيانات من جانب الخادوم في اللزيارة الأولى، والتي ربما لديها اتصال أسرع بقاعدة البيانات من جانب المستخدم. وهذا يؤدي عادة إلى تحسين مقاييس Core Web Vitals، وتجربة مستخدم أفضل، ويمكن أن يكون مهمًا للتطبيقات التي يوصل فيها زمن الوصول إلى المحتوى مباشرة بمعدل التحويل.

  • نموذج ذهني موحد: يمكنك استخدام نفس اللغة ونفس النموذج الذهني التصريحي والموجه بالمكونات لتطوير تطبيقك بأكمله، بدلاً من القفز بين أنظمة القولبة الخلفية وإطار عمل للواجهة الأمامية.

  • أفضل تهيئة لمحركات البحث: سترى معالجات محركات البحث الصفحة بالكامل مصيّرة من جانب الخادوم مباشرة.

    ملاحظة

    حتى الآن، يمكن لـGoogle وـBing فهم تطبيقات JavaScript المتزامنة بشكل جيد. المتزامنة هي الكلمة المفتاحية هنا. إذا بدأ تطبيقك بمؤشر تحميل، ثم جلب المحتوى عبر Ajax، فلن ينتظر معالج محرك البحث أن تنهي التحميل. وهذا يعني أنه إذا كان لديك محتوى يجلب لاتزامنيا على الصفحات التي تكون فيها التهيئة لمحركات البحث مهمة، فقد يكون التصيير من جانب الخادوم ضروريًا.

يوجد أيضًا بعض القيود التي يجب أخذها بعين الاعتبار عند استخدام التصيير من جانب الخادوم:

  • قيود التطوير. يمكن استخدام الشيفرة المخصصة للمتصفح داخل بعض مراحل الدورة الحياة فقط؛ قد تحتاج بعض المكتبات الخارجية إلى معاملة خاصة لتتمكن من التشغيل في تطبيق مصيّر من جانب الخادوم.

  • الكثير من الإعدادات ومتطلبات التوزيع المعقدة. على عكس تطبيق ساكن أحادي الصفحة بالكامل الذي يمكن توزيعه على أي خادوم للملفات الساكنة، يتطلب تطبيق مصيّر من جانب الخادوم بيئة تتيح تشغيل خادوم Node.js.

  • الكثير من التحميل على جانب الخادوم. سيكون تصيير تطبيق كامل في Node.js أكثر تعقيدًا من تقديم الملفات الساكنة فقط، لذا إذا كنت تتوقع حركة طلبات كبيرة، فاستعد للحمل المتناسق على جانب الخادوم واستخدم بصورة ذكية استراتيجيات التخزين المؤقت.

قبل استخدام التصيير من جانب الخادوم لتطبيقك، يجب أن تسأل نفسك أولاً ما إذا كنت بحاجة إليه حقًا. ويعتمد ذلك في الغالب على مدى أهمية زمن الوصول للمحتوى في تطبيقك. على سبيل المثال، إذا كنت تبني لوحة تحكم داخلية حيث لا يهمك كثيرا بضع مئات من أجزاء الثانية الإضافية عند التحميل الأولي، فإن التصيير من جانب الخادوم سيكون خيارا مبالغا فيه. ومع ذلك، في حالات حيث يكون زمن الوصول للمحتوى مهمًا للغاية، يمكن للتصيير من جانب الخادوم مساعدتك على تحقيق أفضل أداء ممكن للتحميل الأولي.

التصيير من جانب الخادوم مقابل توليد الموقع الساكن

توليد الموقع الساكن (SSG)، والذي يعرف أيضًا باسم التصيير المسبق، هو تقنية أخرى شهيرة لبناء مواقع سريعة. إذا كانت البيانات المطلوبة لتصيير صفحة من جانب الخادوم هي نفسها لكل مستخدم، فبدلاً من تصيير الصفحة كل مرة يصل فيها طلب، يمكننا تصييرها مرة واحدة فقط مسبقًا أثناء عملية البناء. تنشأ لصفحات المصيّرة مسبقًا وتقدم كملفات HTML ساكنة.

يحافظ توليد الموقع الساكن على نفس مؤشرات أداء التصيير من جانب الخادوم: يوفر أداءً ممتازًا للوصول إلى المحتوى. في نفس الوقت، فإنه أرخص وأسهل للتوزيع من التطبيقات التي تصيّر من جانب الخادوم لأن المخرجات هي HTML وأصول ساكنة. الكلمة المفتاحية هنا هي ساكنة: لا يمكن تطبيق توليد الموقع الساكن إلا على الصفحات التي تستهلك بيانات ساكنة، أي بيانات معروفة في وقت البناء ولا تتغير بين التوزيعات. في كل مرة تتغير فيها البيانات، يلزم توزيع جديد.

إذا كنت تستكشف التصيير من جانب الخادوم فقط من أجل تحسين التهيئة لمحركات البحث لعدد قليل من الصفحات التسويقية (على سبيل المثال /، /about، /contact، إلخ.)، فمن المحتمل أنك تريد توليد موقع ساكن بدلاً من التصيير من جانب الخادوم. توليد الموقع الساكن أيضًا ممتاز لمواقع الويب المستندة إلى المحتوى مثل مواقع التوثيق أو المدونات. في الواقع، هذا الموقع الذي تقرأه حاليًا يُولد بشكل ساكن باستخدام VitePress، وهو مولد مواقع ساكنة مدعوم من طرف Vue.

دليل تطبيقي أساسي

تصيير تطبيق

لنلق نظرة على أدنى مثال ممكن لتصيير Vue من جانب الخادوم.

  1. أنشأ مجلد جديد وانتقل إليه باستخدام cd
  2. قم بتشغيل npm init -y
  3. أضف "type": "module" في package.json حتى يعمل Node.js في وضع وحدات ES.
  4. قم بتشغيل npm install vue
  5. أنشئ ملف example.js:
js
//يعمل هذا في Node.js على الخادوم.
import { createSSRApp } from 'vue'
// تصدر واجهة برمجة تطبيقات Vue لتصيير من جانب الخادوم تحت `vue/server-renderer`.
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 كوسيط وتعيد وعد يُعطي بعد استيفائه محتوى HTML خاص بالتطبيق. من الممكن أيضًا بث التصيير باستخدام الواجهة البرمجية Node.js Stream أو الواجهة البرمجية لتدفقات الويب. اطلع على مرجع الواجهة البرمجية للتصيير من جانب الخادوم للحصول على التفاصيل الكاملة.

يمكننا بعد ذلك نقل شيفرة تصيير Vue من جانب الخادوم إلى معالج الطلبات، والذي يغلف تمثيل التطبيق مع محتوى 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>مثال للتصيير من جانب الخادوم</title>
      </head>
      <body>
        <div id="app">${html}</div>
      </body>
    </html>
    `)
  })
})

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

في النهاية، قم بتشغيل node server.js وقم بزيارة http://localhost:3000. يجب أن ترى الصفحة تعمل مع الزر.

جربه على StackBlitz

الإنعاش من جانب المستخدم

إذا نقرت على الزر، ستلاحظ أن الرقم لا يتغير. يكون محتوى HTML ثابتًا تمامًا على جانب المستخدم لأننا لا نحمل Vue في المتصفح.

من أجل جعل التطبيق من جانب المستخدم تفاعليًا، تحتاج Vue إلى إجراء عملية الإنعاش. خلال الإنعاش، و تنشئ التطبيق نفسه على جانب المستخدم الذي اشتغل على جانب الخادوم، يتطابق كل مكون مع عناصر الـDOM التي يجب أن تتحكم فيها، بالإضافة إلى ربط مستمعي الأحداث.

من أجل وصل التطبيق في وضع الإنعاش، نحتاج إلى استخدام createSSRApp() بدلاً من createApp():

js
// يشتغل في المتصفح
import { createSSRApp } from 'vue'

const app = createSSRApp({
  // ...نفس التطبيق كما في الخادوم
})

// يفترض وصل تطبيق من جانب الخادوم على جانب المستخدم أن
// يصير HTML مسبقًا وسيقوم بالإنعاش بدلاً من وصل
// عناصر جديدة للـDOM.
app.mount('#app')

هيكلية الشيفرة

لاحظ كيف نحتاج إلى إعادة استخدام نفس الشيفرة التنفيذية كما في جانب الخادوم. هذه هي البداية للتفكير في هيكلية الشيفرة في تطبيق مصيّر من جانب الخادوم - كيف نشارك نفس شيفرة التطبيق بين الخادوم والمستخدم؟

سنعرض هنا أساسيات الإعداد. أولاً، دعونا نقسم شيفرة إنشاء التطبيق إلى ملف مستقل، app.js:

js
// app.js (مشتركة بين الخادوم والمستخدم)
import { createSSRApp } from 'vue'

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

هذا الملف والتبعيات الخاصة به مشتركة بين الخادوم والمستخدم - نسميها الشيفرة الشاملة. هناك عدد من الأشياء التي يجب عليك الاهتمام بها عند كتابة الشيفرة المتنوعة، كما سنتحدث بالتفصيل أدناه.

يقوم الملف من جانب المستخدم باستيراد الشيفرة الشاملة، إنشاء التطبيق، وتنفيذ الوصل:

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

createApp().mount('#app')

ويستخدم الخادوم نفس شيفرة إنشاء التطبيق في معالج الطلب:

js
// server.js (حذفت الشيفرة غير ذي صلة)
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' في المتصفح من خلال إضافة خريطة الاستيراد إلى شيفرة HTML.

جرب تطبيق النموذج المكتمل على StackBlitz. الزر الآن يعمل بشكل تفاعلي!

حلول ذات مستوى عالي

يتطلب الانتقال من النموذج إلى تطبيق SSR جاهز للإنتاج الكثير من الأشياء. سنحتاج إلى:

  • دعم المكونات أحادية الملف ومتطلبات عملية البناء الأخرى. في الواقع، سنحتاج إلى تنسيق بنيتين لنفس التطبيق: واحد للمستخدم، وواحد للخادوم.

    ملاحظة

    تُصرّف مكونات Vue بشكل مختلف عند استخدامها من أجل التصيير من جانب الخادوم - تُصرف القوالب إلى نص متسلسل بدلاً من دالات تصيير الـDOM الافتراضي من أجل أداء تصيير فعال.

  • في معالج الطلب من جانب الخادوم، يُصيّر HTML مع الروابط الصحيحة للموارد من جانب المستخدم وتلميحات الموارد المثلى. قد نحتاج أيضًا إلى التبديل بين وضع التصيير من جانب الخادوم ووضع التصيير من جانب المستخدم، أو حتى دمجهما في نفس التطبيق.

  • إدارة التوجيه، جلب البيانات، وتخزين الحالة من خلال طريقة شاملة.

سيكون التنفيذ الكامل معقدًا جدًا ويعتمد على سلسلة أدوات البناء التي اخترت العمل بها. لذا، ننصح بشدة بالذهاب إلى حلول مُعتدّ بها ذات مستوى عالي، وتخفي الكثير من التعقيدات عنك. سنقدم أدناه بعض الحلول الموصى بها لتصيير من جانب الخادوم في نظام Vue.

إطار Nuxt

Nuxt هو إطار عالي المستوى مبني على نظام Vue ويوفر تجربة تطوير انسيابية لكتابة تطبيقات Vue شاملة. أفضل من ذلك، يمكنك أيضًا استخدامه كمولد لمواقع ساكنة! ننصح بشدة بتجربته.

إطار Quasar

Quasar هو حل كامل يستند على Vue يسمح لك باستهداف SPA، SSR، PWA، تطبيقات الهاتف المحمول، تطبيقات سطح المكتب، وإضافات المتصفح باستخدام نفس الشيفرة الأساسية. لا يتعامل مع إعدادات البناء فقط، بل يوفر مجموعة كاملة من مكونات واجهة المستخدم المتوافقة مع تصميم Material من Google.

Vite SSR

Vite يوفر دعمًا مدمجًا لتصيير من جانب الخادوم لـVue، ولكنه منخفض المستوى بشكل مقصود. إذا كنت ترغب في اعتماد ـVite، فاطلع على vite-plugin-ssr، وهو إضافة من مجتمع Vite تخفي الكثير من التعقيدات عنك.

يمكنك أيضًا العثور على مثال لـVue + Vite SSR باستخدام إعداد يدوي هنا، والذي يمكن أن يكون أساسًا للبناء عليه. تجدر الملاحظة أنه يُوصى به فقط إذا كنت متقنًا للتصيير من جانب الخادوم / أدوات البناء وترغب حقًا في الحصول على تحكم كامل في هيكلية عالية المستوى.

كتابة شيفرة متوافقة مع التصيير من جانب الخادوم

بغض النظر عن إعدادات البناء أو اختيار إطار عالي المستوى، فهناك بعض المبادئ التي تنطبق على جميع تطبيقات المصيرة من جانب الخادوم.

التفاعلية على مستوى الخادوم

أثناء التصيير من جانب الخادوم، يعين كل طلب عبر عنوان URL يعبر عن حالة مطلوبة من التطبيق. لا يوجد تفاعل للمستخدم ولا تحديثات DOM، لذلك لا يلزم التفاعل على الخادوم. افتراضيًا، يتم يعطل التفاعل خلال التصيير من جانب الخادوم لتحسين الأداء.

خطافات دورة حياة المكون

نظرًا لعدم وجود تحديثات ديناميكية، فلن تستدعى خطافات دورة حياة المكون مثل mountedonMounted أو updatedonUpdated خلال التصيير من جانب الخادوم وستنفذ فقط على المتصفح. الخطافات الوحيدة التي تستدعى خلال التصيير من جانب الخادوم هي beforeCreate و created

يجب عليك تجنب الشيفرة التي تنتج تأثيرات جانبية تحتاج إلى تنظيفها في beforeCreate و createdsetup() أو نطاق الجذر <script setup>. مثال على تلك التأثيرات الجانبية هو إعداد المؤقتات باستخدام setInterval. في الشيفرة المخصصة للمتصفح فقط، يمكننا إعداد المؤقت ومن ثم إزالته في beforeUnmountonBeforeUnmount أو unmountedonUnmounted. ومع ذلك، لأن خطافات إزالة التثبيت لن تستدعى أبدًا خلال التصيير من جانب الخادوم، فستبقى المؤقتات هناك إلى الأبد. لتجنب ذلك، نقل الشيفرة التي تنتج تأثيرات جانبية إلى mountedonMounted بدلاً من ذلك.

الوصول إلى الواجهات البرمجية الخاصة بالمنصة

لا يمكن للشيفرة الشاملة الوصول إلى واجهات برمجية خاصة بالمنصة، لذلك إذا كانت الشيفرة الخاصة تستخدم الكائنات العامة المخصصة للمتصفح مثل window أو document، فستطلق أخطاء عند تنفيذها في Node.js، أو العكس.

للمهام المشتركة بين الخادوم والمستخدم ولكن مع اختلاف الواجهات برمجية للمنصة، ينصح بتغليف الشيفرات التنفيذية المخصصة للمنصة داخل واجهة برمجية شاملة، أو استخدام المكتبات التي تفعل ذلك نيابة لك. على سبيل المثال، يمكنك استخدام node-fetch لاستخدام نفس الواجهة البرمجية لجلب البيانات على الخادوم والمستخدم.

بالنسبة للواجهات البرمجية الخاصة بالمتصفح فقط، فإن النهج الشائع هو الوصول إليهم بشكل كسول داخل خطافات دورة حياة المخصصة للمستخدم مثل mountedonMounted.

تجدر الإشارة إلى أنه إذا لم يكنت المكتبة الخارجية مكتوبة بنظرة شاملة للاستخدام، فقد يكون من الصعب تكاملها في تطبيق تصيير من جانب الخادوم. قد تتمكن من الحصول على العمل عن طريق تقليد بعض الكائنات العامة، ولكن سيكون ذلك مختلفًا وقد يتداخل مع رمز التحديد البيئي للمكتبات الأخرى.

تلوث الحالة عبر الطلبات

في محور إدارة الحالة، قدمنا نمط إدارة حالة بسيط باستخدام الواجهات البرمجية التفاعلية. في سياق تصيير من جانب الخادوم، يتطلب هذا النمط بعض التعديلات إضافية.

يعين النمط الحالة المشتركة في نطاق جذر وحدة الـJavaScript. يجعل هذا النمط نسخة واحد - أي وجود نسخة واحدة فقط من الكائن التفاعلي عبر جميع دورات حياة التطبيق. يعمل هذا كما هو متوقع في تطبيق Vue الخاص بالمتصفح فقط، لأن الوحدات في التطبيق تتم تهيئتها بشكل جديد لكل زيارة لصفحة المتصفح.

ومع ذلك، في سياق تصيير من جانب الخادوم، تهيأ وحدات التطبيق مرة واحدة فقط على الخادوم عند تشغيله. ستُستخدم نفس وحدات التطبيق عبر عدة طلبات من جانب الخادوم، وبالتالي ستستخدم الحالة المشتركة النسخة الواحدة. إذا غيرنا الحالة المشتركة النسخة واحدة ببيانات مخصصة لمستخدم واحد، فقد تُسرَّب بشكل عرضي إلى طلب من مستخدم آخر. نسمي هذا تلوث الحالة عبر الطلبات.

يمكننا تقنيًا إعادة تهيئة جميع وحدات الـJavaScript على كل طلب، مثل ما نفعله في المتصفحات. ومع ذلك، يمكن أن تكون تهيئة وحدات الـJavaScript مكلفة، لذا سيؤثر هذا على أداء الخادوم بشكل كبير.

الحل الموصى به هو إنشاء نسخة جديدة من التطبيق بأكمله - بما في ذلك الموجه والمخازن العامة - على كل طلب. ثم، بدلاً من استيراده مباشرة في مكوناتنا، نقدم الحالة المشتركة باستخدام الدالة provide على مستوى جذر التطبيق ونحقنه في المكونات التي تحتاج إليها:

js
// app.js (مشتركة بين الخادوم والمستخدم)
import { createSSRApp } from 'vue'
import { createStore } from './store.js'

// تستدعى في كل طلب
export function createApp() {
  const app = createSSRApp(/* ... */)
  // انشاء نسخة جديدة من المخزن لكل طلب
  const store = createStore(/* ... */)
  // تقديم المخزن على مستوى جذر التطبيق
  app.provide('store', store)
  // تعريف المخزن أيضًا لأغراض الانعاش
  return { app, store }
}

مكتبات إدارة الحالة مثل Pinia مصممة لذلك. اطلع على دليل Pinia لتصيير من جانب الخادوم لمزيد من التفاصيل.

عدم توافق الإنعاش

إذا لم يتطابق تركيب الـ DOM للـ HTML المصير مسبقًا مع الناتج المتوقع للتطبيق من جانب المستخدم، فسيحدث خطأ في عملية الإنعاش. ينتج عدم توافق الإنعاش بشكل شائع للأسباب التالية:

  1. يحتوي القالب على تركيب HTML غير صالح للتداخل، و أُصلح الـ HTML المصير من قبل سلوك تحليل HTML الأصلي للمتصفح. على سبيل المثال، يمكن أن يكون الخطأ الشائع هو أن <div> لا يمكن وضعه داخل <p>:

    html
    <p><div>مرحبا</div></p>

إذا أنتجنا هذا في الـHTML المصير من الخادوم، سيقوم المتصفح بإنهاء <p> الأول عندما يُتعرف على <div> ويحلله إلى تركيب الـ DOM التالي:

html
<p></p>
<div>مرحبا</div>
<p></p>
  1. تحتوي البيانات المستخدمة أثناء التصيير على قيم عشوائية. لأن نفس التطبيق سيشغل مرتين - مرة واحدة على الخادوم، ومرة واحدة على المستخدم - لا تُضمن أن القيم العشوائية هي نفسها في الجانبين. هناك طريقتان لتجنب عدم توافق القيم العشوائية:

    1. استخدم v-if + onMounted لتصيير الجزء الذي يعتمد على القيم العشوائية فقط على جانب المستخدم. قد يكون لديك إطار عمل يوفر ميزات مبنية لتسهيل هذا، على سبيل المثال المكون <ClientOnly> في VitePress.

    2. استخدم مكتبة مولد الأرقام العشوائية التي تدعم الإنتاج مع توليد البيانات، وضمان استخدام الجانبين لنفس البيانات (على سبيل المثال، بتضمين البيانات في الحالة المتسلسلة واسترجاعها على جانب المستخدم).

  2. الخادوم والمستخدم في مناطق زمنية مختلفة. في بعض الأحيان، قد نرغب في تحويل الطابع الزمني إلى الوقت المحلي للمستخدم. ومع ذلك، لا تكون المنطقة الزمنية خلال تشغيل الخادوم والمنطقة الزمنية خلال تشغيل المستخدم دائمًا نفسها، وقد لا نعرف بشكل موثوق المنطقة الزمنية للمستخدم خلال تشغيل الخادوم. في هذه الحالات، يجب أيضًا تنفيذ تحويل الوقت المحلي كعملية مستخدم فقط.

عند تعرض Vue لعدم توافق التصيير، سيحاول إصلاحه وتعديل الـ DOM المصير من الخادوم ليتوافق مع حالة جانب المستخدم. سيؤدي هذا إلى تضييع بعض أداء التصيير بسبب تجاهل العقد الخاطئة وتثبيت العقد الجديدة، ولكن في معظم الحالات، يجب أن يواصل التطبيق العمل كما هو متوقع. ومع ذلك، فإنه من الأفضل إزالة عدم توافق التصيير خلال التطوير.

الموجهات المخصصة

بما أن معظم الموجهات المخصصة تتضمن التحكم المباشر بالـ DOM، فإنها متجاهلة خلال التصيير. ومع ذلك، إذا كنت ترغب في تحديد كيفية تصيير الموجهة المخصصة (على سبيل المثال، ما هي السمات التي يجب إضافتها إلى العنصر المصير)، يمكنك استخدام مرشح getSSRProps للموجهة:

js
const myDirective = {
  mounted(el, binding) {
    // التنفيذ على جانب المستخدم:
    // تحديث الـ DOM مباشرة
    el.id = binding.value
  },
  getSSRProps(binding) {
    // التنفيذ على جانب الخادوم:
    // إرجاع الخصائص المصيرة.
    // يتلقى getSSRProps مجرد الربط للموجهة.
    return {
      id: binding.value
    }
  }
}

مكونات الـTeleports

تتطلب مكونات الـ Teleports معالجة خاصة خلال التصيير. إذا كان التطبيق المصير يحتوي على مكونات Teleports، فلن يكون المحتوى المنقول جزءًا من السلسلة المصيرة. يمكنك استخدام حل أسهل عن طريق تقديم الـ Teleport بشكل شرطي عند التثبيت.

إذا كنت بحاجة إلى تصيير المحتوى المنقول، فسيتوفر بالخاصية teleports لكائن سياق التصيير:

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

console.log(ctx.teleports) // { '#teleported': 'المحتوى المنقول' }

تحتاج إلى حقن المحتوى المنقول في الموقع الصحيح في HTML الصفحة النهائية مثلما تحتاج إلى حقن محتوى التطبيق الرئيسي.

ملاحظة

تجنب تحديد body عند استخدام Teleports والتصيير معًا - عادةً ما يحتوي <body> على محتوى أخر صُير من الخادوم والذي يجعل من غير الممكن للـ Teleports تحديد الموقع الصحيح لبدء التصيير.

بدلاً من ذلك، استخدم عنصر حاوي مخصص، على سبيل المثال <div id="teleported"></div> الذي يحتوي فقط على المحتوى المنقول.

التصيير من جانب الخادوم (SSR) has loaded