| اسم الملف المعماري الجديد | دوره الوظيفي الأساسي في البيئة المزدوجة | موقع تواجده في هيكل المشروع |
|---|---|---|
| `server.ts` | إعداد خادم `Express` محلياً لتلقي طلبات المتصفح وتوليد صفحات الـ SSR | في المجلد الرئيسي للمشروع (`Root`) |
| `app.config.server.ts` | دمج وتوفير مزودات بيئة الخادم الأساسية مثل `provideServerRendering` | داخل مجلد `app` |
| `main.server.ts` | نقطة انطلاق وتوجيه كود التطبيق الأساسي للعمل على الخادم | داخل مجلد `src` |
بدلاً من بناء إعدادات الخادم المعقدة يدوياً، يتيح لك نظام Angular 22.1 وأداة سطر الأوامر (Angular CLI) إضافة وتهيئة بيئة العرض على الخادم (SSR) في المشروع بأمر برمجي واحد ومباشر. يقوم هذا الأمر بإنشاء كافة الملفات والمجلدات اللازمة لربط التطبيق ببيئة Node.js وإعداد ملفات البناء المخصصة للخادم بكل سلاسة.
استخدام الأمر ng add @angular/ssr يتكفل بتعديل ملفات angular.json وإضافة كافة الاعتمادات والمزودات المطلوبة تلقائياً.
يتم فصل نقطة انطلاق العميل عن نقطة انطلاق الخادم، مما يضمن توجيه الطلبات الواردة إلى الخادم لتوليد الصفحة قبل إرسالها.
| 1 | # تنفيذ أمر إضافة حزمة العرض على الخادم عبر أداة أنجولر |
| 2 | ng add @angular/ssr |
⚠️ تحذير
تذكر أن تعديل مسارات مجلد التوزيع (dist) يدوياً في ملف server.ts قد يتسبب في فشل خادم الـ Node في العثور على ملفات المتصفح! احرص دائماً على الالتزام بالهيكل الافتراضي الذي يولده نظام الأنجولر عند تهيئة الـ SSR.
| 1 | import { APP_BASE_HREF } from '@angular/common'; |
| 2 | import { CommonEngine } from '@angular/ssr/node'; |
| 3 | import express from 'express'; |
| 4 | import { fileURLToPath } from 'node:url'; |
| 5 | import { dirname, join, resolve } from 'node:path'; |
| 6 | import bootstrap from './src/main.server'; |
| 7 | |
| 8 | export function app(): express.Express { |
| 9 | const server = express(); |
| 10 | const serverDistFolder = dirname(fileURLToPath(import.meta.url)); |
| 11 | const browserDistFolder = resolve(serverDistFolder, '../browser'); |
| 12 | const indexHtml = join(browserDistFolder, 'index.html'); |
| 13 | |
| 14 | const commonEngine = new CommonEngine(); |
| 15 | |
| 16 | server.set('view engine', 'html'); |
| 17 | server.set('views', browserDistFolder); |
| 18 | |
| 19 | // التعامل مع طلبات المسارات وعرض صفحات SSR |
| 20 | server.get('*.*', express.static(browserDistFolder, { |
| 21 | maxAge: '1y' |
| 22 | })); |
| 23 | |
| 24 | server.get('*', (req, res, next) => { |
| 25 | const { protocol, originalUrl, baseUrl, headers } = req; |
| 26 | |
| 27 | commonEngine |
| 28 | .render({ |
| 29 | bootstrap, |
| 30 | documentFilePath: indexHtml, |
| 31 | url: `${protocol}://${headers.host}${originalUrl}`, |
| 32 | publicPath: browserDistFolder, |
| 33 | providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], |
| 34 | }) |
| 35 | .then((html) => res.send(html)) |
| 36 | .catch((err) => next(err)); |
| 37 | }); |
| 38 | |
| 39 | return server; |
| 40 | } |
| 41 | |
| 42 | function run(): void { |
| 43 | const port = process.env['PORT'] || 4000; |
| 44 | const server = app(); |
| 45 | server.listen(port, () => { |
| 46 | console.log(`Node Express server listening on http://localhost:${port}`); |
| 47 | }); |
| 48 | } |
| 49 | |
| 50 | run(); |