Node.js: قوالب و SSR

آخر تحديث: 2026-08-26

1. القصة: واجهة أداة إدارة تشارلي

Charlie is a バックエンド developer at a startup. The team needed an internal management tool to view orders, user statistics, and system status. His colleagues suggested using React or Vue, but Charlie felt those frameworks were too heavy for an internal tool like this—with just a few pages and simple interactions, server-side rendering with a template engine would suffice. He chose EJS because it’s essentially just HTML with a few tags, making it incredibly easy to get started. Within a day, he had built a complete admin interface with reusable layouts using Express and EJS.

ستتعلم:



2. عملية عرض القوالب

تتمثل الوظيفة الأساسية لمحرك القوالب في دمج ملفات القوالب مع البيانات لإنشاء سلسلة HTML النهائية، التي يتم إرجاعها بعد ذلك إلى المتصفح.

100%
flowchart LR
    A[Browser Request] --> B[Express Routing]
    B --> C[Retrieve Data]
    C --> D["res.render()"]
    D --> E[EJS / Pug Compilation]
    E --> F[Generate HTML]
    F --> G[HTTP Response]

يمكن تلخيص العملية برمتها على النحو التالي:

  1. يرسل المتصفح طلب HTTP
  2. يتم ربط المسارات السريعة بوظائف المعالجة المقابلة لها
  3. يقوم المُعالج باسترداد البيانات من قاعدة بيانات أو واجهة برمجة تطبيقات (API)
  4. استدعاء res.render() وتمرير اسم القالب والبيانات إليه
  5. يقوم محرك القوالب بتحويل القالب والبيانات إلى لغة HTML
  6. يتم إرجاع كود HTML إلى المتصفح كاستجابة HTTP


3. القواعد الأساسية لـ EJS

يُعد EJS (جافا سكريبت المدمج) محرك القوالب الأكثر سهولة في الاستخدام — فالقوالب هي ببساطة كود HTML عادي، مع منطق مدمج باستخدام علامات <% %>.

▶ مثال: العلامات الأربعة الأساسية في EJS

JAVASCRIPT
// <%= %> Output the escaped value (Safe, prevents XSS)
<p>Hello, <%= userName %>!</p>

// <% %> Execution Logic,No output
<% if (isAdmin) { %>
  <span>Admin Panel</span>
<% } %>

// <%- %> Output Raw HTML (Not escaped, use with caution)
<%- articleHtml %>

// <%- include %> Importing Subtemplates
<%- include('partials/header') %>
▶ جرّب الكود

(1) مرجع سريع لقواعد بناء الجمل في EJS

العلامة الوظيفة الناتج رمز الهروب مثال
<%= %> قيمة المتغير الناتج نعم نعم <%= name %>
<%- %> إنتاج محتوى أصلي نعم لا <%- html %>
<% %> منطق التنفيذ لا <% if (x) { %>
<%# %> تعليق لا <%# comment %>
<%- include('x') %> تضمين القوالب الفرعية نعم لا <%- include('nav') %>

▶ مثال: الحلقات والشروط في EJS

JAVASCRIPT
<ul>
  <% items.forEach(function(item) { %>
    <li>
      <strong><%= item.name %></strong>
      <% if (item.onSale) { %>
        <span class="badge">Sale</span>
      <% } %>
    </li>
  <% }); %>
</ul>
▶ جرّب الكود

4. تكوين Express + EJS

▶ Example: Setting Up a Complete Express + EJS Project

Project Structure:

TEXT 📖 للعرض فقط
views/
  partials/
    header.ejs
    footer.ejs
  layout.ejs
  index.ejs
app.js
BASH
npm init -y
npm install express ejs
JAVASCRIPT
const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Dashboard',
    user: { name: 'Charlie', role: 'admin' },
    stats: { orders: 128, users: 56 }
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

(1) Key API Documentation

واجهة برمجة التطبيقات (API) الغرض مثال
app.set('view engine', 'ejs') تعيين محرك القوالب الافتراضي لا داعي لإدراج امتداد الملف عند العرض
app.set('views', path) تعيين دليل القوالب القيمة الافتراضية هي ./views
res.render(view, data) عرض القالب والرد res.render('index', {title: 'Hi'})
res.render(view, data, callback) استرداد سلسلة HTML بعد العرض يمكن استخدامها لإرسال رسائل البريد الإلكتروني وفي حالات أخرى

▶ مثال: تمرير البيانات كمعلمة ثانية إلى res.render

JAVASCRIPT
app.get('/profile/:id', (req, res) => {
  const user = { id: req.params.id, name: 'Alice', bio: 'Full-stack dev' };
  res.render('profile', {
    user: user,
    pageTitle: user.name + "'s Profile",
    isLoggedIn: true
  });
});
▶ جرّب الكود

الوصول إلى المتغيرات مباشرةً بالاسم في القالب:

JAVASCRIPT
<h1><%= pageTitle %></h1>
<p>Name: <%= user.name %></p>
<p>Bio: <%= user.bio %></p>


5. القواعد الأساسية للغة Pug

تستخدم لغة Pug (المعروفة سابقًا باسم Jade) المسافات البادئة بدلاً من العلامات الختامية، مما يجعل صيغتها موجزة للغاية، لكن منحنى تعلمها أكثر حدة مقارنةً بلغة EJS.

▶ مثال: قواعد اللغة الأساسية في لغة Pug

TEXT 📖 للعرض فقط
//- Pug Template
doctype html
html
  head
    title= pageTitle
  body
    h1= message
    p Welcome to #{siteName}

HTML المُجمَّع:

TEXT 📖 للعرض فقط
<!DOCTYPE html>
<html>
  <head><title>My Page</title></head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to MySite</p>
  </body>
</html>

(1) مرجع سريع لقواعد لغة Pug

الصيغة الوظيفة مثال
tag= value ربط الخصائص h1= title
#{expr} الاستيفاء p Hello #{name}
- code تنفيذ JS - const x = 1
if/else if/else الحالة if (admin) ...
each val in arr حلقة each item in list
extends layout وراثة التخطيط extends layout
block name تعريف/ملء الكتلة block content
include path استيراد القوالب الفرعية include header

▶ مثال: الحلقات والشروط في لغة Pug

TEXT 📖 للعرض فقط
ul
  each item in items
    li
      strong= item.name
      if item.onSale
        span.badge Sale


6. إعادة استخدام العروض الجزئية والتخطيطات

تكمن القيمة الأساسية لمحرك القوالب في تجنب الحاجة إلى تكرار الرأس والتذييل وهيكل HTML في كل صفحة.

▶ مثال: تخطيط EJS + عرض جزئي

views/partials/header.ejs

JAVASCRIPT
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
▶ جرّب الكود

views/partials/footer.ejs

JAVASCRIPT
<footer>&copy; 2026 My App</footer>

views/layout.ejs

JAVASCRIPT
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('partials/header') %>
  <main>
    <%- body %>
  </main>
  <%- include('partials/footer') %>
</body>
</html>

لا تتضمن EJS آلية تخطيط مدمجة؛ لذا يجب تنفيذها باستخدام البرمجيات الوسيطة express-ejs-layouts أو عن طريق تضمين الملفات يدويًّا.

BASH
npm install express-ejs-layouts
JAVASCRIPT
const expressLayouts = require('express-ejs-layouts');
app.use(expressLayouts);
app.set('layout', 'layout');

▶ مثال: وراثة تخطيط Pug

views/layout.pug

TEXT 📖 للعرض فقط
doctype html
html
  head
    title= title
    link(rel="stylesheet" href="/style.css")
  body
    include partials/header
    block content
    include partials/footer

views/index.pug

TEXT 📖 للعرض فقط
extends layout

block content
  h1= pageTitle
  p Welcome!

(1) مقارنة بين استراتيجيات التخطيط

العقارات EJS بوج
وراثة التخطيط يتطلب برمجيات وسيطة express-ejs-layouts أصلي extends + block
استيراد محلي <%- include('partial') %> include partial
تمرير المعلمات إلى القوالب الفرعية <%- include('x', {data}) %> تمرير البيانات عبر القالب الأصلي
دعم الكتل المتعددة محدود دعم أصلي للكتل المتعددة


7. مقارنة بين محركات القوالب

(1) EJS مقابل Pug مقابل Handlebars

ميزة EJS Pug Handlebars
أسلوب الصياغة علامات HTML + JS مع مسافة بادئة، بدون علامات إغلاق Mustache {{}}
منحنى التعلم الأدنى المتوسط المنخفض
الاستدلال المنطقي جافا سكريبت كامل جافا سكريبت كامل مقيد (لا يُسمح باستخدام جافا سكريبت تعسفي)
الهندسة/التوريث يتطلب برامج وسيطة دعم أصلي يتطلب تهيئة إضافية
مخرج الهروب <%= %> = أو #{} {{}}
الناتج الأصلي <%- %> !{} {{{}}}
حالات الاستخدام البدء السريع / الأدوات الداخلية البساطة أولاً / المشاريع الشاملة الأمن أولاً / العرض على أجهزة متعددة
نشاط المجتمع مرتفع مرتفع متوسط

  1. مقارنة بين إعادة تنظيم القطاع الأمني (SSR) والمسؤولية الاجتماعية للشركات (CSR)

(1) جدول الاختلافات الرئيسية

البعد SSR (العرض من جانب الخادم) CSR (العرض من جانب العميل)
موقع العرض الخادم المتصفح
سرعة التحميل عند فتح الصفحة لأول مرة سريعة (يتم تحميل HTML فورًا) بطيئة (يجب الانتظار حتى يتم تحميل JS وتنفيذه)
ملاءمة محركات البحث عالية منخفضة (قد لا يكون المحتوى مرئيًا لبرامج الزحف)
تعقيد التفاعل منخفض (يتم تحديث الصفحة مع كل طلب) مرتفع (SPA، لا يتم تحديث الصفحة)
حمل الخادم مرتفع منخفض
التقنيات الشائعة EJS / Pug / PHP React / Vue / Angular
حالات الاستخدام الأدوات الداخلية / المدونات / صفحات تحسين محركات البحث (SEO) التفاعلات المعقدة / تطبيقات SPA

▶ مثال: الاختلافات في الردود بين SSR و CSR

استجابة SSR — يتلقى المتصفح كود HTML الكامل مباشرةً:

TEXT 📖 للعرض فقط
GET /about
→ Server-Side Rendering HTML Return in Full
→ Display directly in the browser,No need to wait JS

استجابة CSR — يتلقى المتصفح هيكلًا فارغًا + حزمة JS:

TEXT 📖 للعرض فقط
GET /about
→ Server Response <div id="app"></div> + bundle.js
→ Browser Download JS,Execute Rendering
→ Users view the content(Duration of the white screen)


8. مثال شامل: صفحة مدونة EJS

قم بإنشاء تطبيق مدونة كامل باستخدام Express و EJS، يتضمن تخطيطات قابلة لإعادة الاستخدام، وقائمة بالمشاركات، وتفاصيل المشاركات، والتنقل المحلي.

▶ مثال:(1) هيكل المشروع

TEXT 📖 للعرض فقط
blog/
  views/
    partials/
      header.ejs
      footer.ejs
      nav.ejs
    index.ejs
    post.ejs
  app.js

▶ مثال: نقطة دخول التطبيق app.js

JAVASCRIPT
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');
app.use(expressLayouts);
app.set('layout', 'layout');

const posts = [
  { id: 1, title: 'Getting Started with Node.js', author: 'Charlie', date: '2026-06-20', excerpt: 'Learn the basics of Node.js runtime.' },
  { id: 2, title: 'Understanding Express Middleware', author: 'Alice', date: '2026-06-25', excerpt: 'Deep dive into middleware patterns.' },
  { id: 3, title: 'Template Engines Compared', author: 'Bob', date: '2026-07-01', excerpt: 'EJS vs Pug vs Handlebars showdown.' }
];

app.get('/', (req, res) => {
  res.render('index', { title: 'Blog Home', posts: posts });
});

app.get('/post/:id', (req, res) => {
  const post = posts.find(p => p.id === parseInt(req.params.id));
  if (!post) return res.status(404).send('Post not found');
  res.render('post', { title: post.title, post: post });
});

app.listen(3000, () => {
  console.log('Blog running on http://localhost:3000');
});
▶ جرّب الكود

▶ مثال: ملفات «partials/nav.ejs» الخاصة بالتنقل المحلي

JAVASCRIPT
<nav class="blog-nav">
  <a href="/">All Posts</a>
  <% posts.forEach(function(p) { %>
    <a href="/post/<%= p.id %>"><%= p.title %></a>
  <% }); %>
</nav>
▶ جرّب الكود

▶ مثال: ملف الرأس الجزئي المحلي partials/header.ejs

JAVASCRIPT
<header class="site-header">
  <h1><%= title %></h1>
</header>
▶ جرّب الكود
JAVASCRIPT
<footer class="site-footer">
  <p>&copy; 2026 My Blog. Powered by Express + EJS.</p>
</footer>
▶ جرّب الكود

▶ مثال: عدد مشاهدات صفحة قائمة المقالات/index.ejs

JAVASCRIPT
<h2>Latest Posts</h2>
<div class="post-list">
  <% posts.forEach(function(post) { %>
    <article class="post-card">
      <h3><a href="/post/<%= post.id %>"><%= post.title %></a></h3>
      <p class="meta">By <%= post.author %> on <%= post.date %></p>
      <p><%= post.excerpt %></p>
    </article>
  <% }); %>
</div>
▶ جرّب الكود

▶ مثال: عدد مشاهدات صفحة تفاصيل المنشور/post.ejs

JAVASCRIPT
<h2><%= post.title %></h2>
<p class="meta">By <%= post.author %> on <%= post.date %></p>
<div class="post-body">
  <p><%= post.excerpt %></p>
  <p>Full article content goes here...</p>
</div>
<a href="/">&larr; Back to all posts</a>
▶ جرّب الكود

كيف يبدو الشكل بعد تشغيل التطبيق:

TEXT 📖 للعرض فقط
http://localhost:3000       → Article List Page
http://localhost:3000/post/1 → Article Details Page
http://localhost:3000/post/2 → Second Article


❓ أسئلة شائعة

س هل لا يزال SSR ضروريًا اليوم؟
ج لا يزال SSR الخيار المثالي لأدوات الإدارة الداخلية، والصفحات التي تتطلب معايير عالية لتحسين محركات البحث (مثل المدونات وصفحات منتجات التجارة الإلكترونية)، والمواقع البسيطة التي تركز على المحتوى أولاً — دون التعقيدات الإضافية التي تفرضها أطر عمل الواجهة الأمامية.
س أيهما أفضل، EJS أم Pug؟
ج EJS أقرب إلى لغة HTML الأصلية، ويتميز بسهولة التعلم، وهو مناسب للتطوير السريع؛ أما Pug فيتميز ببنية لغوية أكثر إيجازًا، لكنه يتطلب التعود على قواعد المسافات البادئة الخاصة به، مما يجعله مناسبًا للفرق التي تولي أولوية لإيجاز الكود. يُنصح باستخدام EJS للمبتدئين.
س كيف يمكنني إعادة استخدام التخطيطات في القوالب؟
ج يستخدم EJS البرمجية الوسيطة express-ejs-layouts لتنفيذ وراثة التخطيطات، أو يمكنك استيراد العروض الجزئية يدويًّا باستخدام <%- include() %>؛ أما Pug فيدعم بشكل أصلي extends + block لتنفيذ وراثة التخطيطات.
س ما هي المعلمة الثانية لـ res.render؟
ج إنها كائن بيانات يمكن استخدام خصائصه مباشرةً كمتغيرات في القالب، مثل res.render('index', {title: 'Hi'})؛ وفي القالب، يمكنك كتابة <%= title %>.
س هل يدعم محرك القوالب ميزة إعادة التحميل الفوري؟
ج أثناء التطوير، يمكنك استخدام nodemon لمراقبة التغييرات التي تطرأ على الملفات وإعادة تشغيل الخدمة تلقائيًا. كما يمكنك استخدام البرمجيات الوسيطة livereload لتمكين التحديث التلقائي للمتصفح، مما يغنيك عن الحاجة إلى تحديث الصفحة يدويًّا.
س ما الفرق بين <%- و <%=؟
ج <%= يعرض القيم بعد تهيئة HTML لمنع هجمات XSS؛ وهذا هو الخيار الآمن. <%- يعرض المحتوى الخام دون تهيئة، ويجب استخدامه فقط عندما يكون المحتوى موثوقًا به أو عند تضمين قوالب فرعية.
س هل يمكنني استخدام async/await في القوالب؟
ج لا يتم دعم await على المستوى الأعلى في قوالب EJS. يجب عليك إتمام جميع عمليات استرداد البيانات غير المتزامنة في المسار، ثم تمرير النتائج إلى القالب باستخدام res.render().

📖 ملخص


📝 تمارين

  1. أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
  2. قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
  3. راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
  4. التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
  5. حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%