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.
ستتعلم:
- العلامات النحوية الأربعة الأساسية في EJS واستخداماتها
- كيفية تكوين التكامل بين EJS و Pug في Express
- قواعد التبعية في لغة Pug، والمتغيرات، والحلقات، والشروط
- العملية الكاملة لعرض القوالب
- استراتيجيات لإعادة استخدام العروض الجزئية والتخطيطات
- كيفية نقل البيانات من جهاز التوجيه إلى القالب
- الاختلافات الرئيسية بين SSR وCSR ومعايير الاختيار
2. عملية عرض القوالب
تتمثل الوظيفة الأساسية لمحرك القوالب في دمج ملفات القوالب مع البيانات لإنشاء سلسلة HTML النهائية، التي يتم إرجاعها بعد ذلك إلى المتصفح.
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]
يمكن تلخيص العملية برمتها على النحو التالي:
- يرسل المتصفح طلب HTTP
- يتم ربط المسارات السريعة بوظائف المعالجة المقابلة لها
- يقوم المُعالج باسترداد البيانات من قاعدة بيانات أو واجهة برمجة تطبيقات (API)
- استدعاء
res.render()وتمرير اسم القالب والبيانات إليه - يقوم محرك القوالب بتحويل القالب والبيانات إلى لغة HTML
- يتم إرجاع كود HTML إلى المتصفح كاستجابة HTTP
3. القواعد الأساسية لـ EJS
يُعد EJS (جافا سكريبت المدمج) محرك القوالب الأكثر سهولة في الاستخدام — فالقوالب هي ببساطة كود HTML عادي، مع منطق مدمج باستخدام علامات <% %>.
▶ مثال: العلامات الأربعة الأساسية في EJS
// <%= %> 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
<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:
views/
partials/
header.ejs
footer.ejs
layout.ejs
index.ejs
app.js
npm init -y
npm install express ejs
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
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
});
});
الوصول إلى المتغيرات مباشرةً بالاسم في القالب:
<h1><%= pageTitle %></h1>
<p>Name: <%= user.name %></p>
<p>Bio: <%= user.bio %></p>
5. القواعد الأساسية للغة Pug
تستخدم لغة Pug (المعروفة سابقًا باسم Jade) المسافات البادئة بدلاً من العلامات الختامية، مما يجعل صيغتها موجزة للغاية، لكن منحنى تعلمها أكثر حدة مقارنةً بلغة EJS.
▶ مثال: قواعد اللغة الأساسية في لغة Pug
//- Pug Template
doctype html
html
head
title= pageTitle
body
h1= message
p Welcome to #{siteName}
HTML المُجمَّع:
<!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
ul
each item in items
li
strong= item.name
if item.onSale
span.badge Sale
6. إعادة استخدام العروض الجزئية والتخطيطات
تكمن القيمة الأساسية لمحرك القوالب في تجنب الحاجة إلى تكرار الرأس والتذييل وهيكل HTML في كل صفحة.
▶ مثال: تخطيط EJS + عرض جزئي
views/partials/header.ejs
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
views/partials/footer.ejs
<footer>© 2026 My App</footer>
views/layout.ejs
<!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أو عن طريق تضمين الملفات يدويًّا.
npm install express-ejs-layouts
const expressLayouts = require('express-ejs-layouts');
app.use(expressLayouts);
app.set('layout', 'layout');
▶ مثال: وراثة تخطيط Pug
views/layout.pug
doctype html
html
head
title= title
link(rel="stylesheet" href="/style.css")
body
include partials/header
block content
include partials/footer
views/index.pug
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 {{}} |
| منحنى التعلم | الأدنى | المتوسط | المنخفض |
| الاستدلال المنطقي | جافا سكريبت كامل | جافا سكريبت كامل | مقيد (لا يُسمح باستخدام جافا سكريبت تعسفي) |
| الهندسة/التوريث | يتطلب برامج وسيطة | دعم أصلي | يتطلب تهيئة إضافية |
| مخرج الهروب | <%= %> |
= أو #{} |
{{}} |
| الناتج الأصلي | <%- %> |
!{} |
{{{}}} |
| حالات الاستخدام | البدء السريع / الأدوات الداخلية | البساطة أولاً / المشاريع الشاملة | الأمن أولاً / العرض على أجهزة متعددة |
| نشاط المجتمع | مرتفع | مرتفع | متوسط |
- مقارنة بين إعادة تنظيم القطاع الأمني (SSR) والمسؤولية الاجتماعية للشركات (CSR)
(1) جدول الاختلافات الرئيسية
| البعد | SSR (العرض من جانب الخادم) | CSR (العرض من جانب العميل) |
|---|---|---|
| موقع العرض | الخادم | المتصفح |
| سرعة التحميل عند فتح الصفحة لأول مرة | سريعة (يتم تحميل HTML فورًا) | بطيئة (يجب الانتظار حتى يتم تحميل JS وتنفيذه) |
| ملاءمة محركات البحث | عالية | منخفضة (قد لا يكون المحتوى مرئيًا لبرامج الزحف) |
| تعقيد التفاعل | منخفض (يتم تحديث الصفحة مع كل طلب) | مرتفع (SPA، لا يتم تحديث الصفحة) |
| حمل الخادم | مرتفع | منخفض |
| التقنيات الشائعة | EJS / Pug / PHP | React / Vue / Angular |
| حالات الاستخدام | الأدوات الداخلية / المدونات / صفحات تحسين محركات البحث (SEO) | التفاعلات المعقدة / تطبيقات SPA |
▶ مثال: الاختلافات في الردود بين SSR و CSR
استجابة SSR — يتلقى المتصفح كود HTML الكامل مباشرةً:
GET /about
→ Server-Side Rendering HTML Return in Full
→ Display directly in the browser,No need to wait JS
استجابة CSR — يتلقى المتصفح هيكلًا فارغًا + حزمة JS:
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) هيكل المشروع
blog/
views/
partials/
header.ejs
footer.ejs
nav.ejs
index.ejs
post.ejs
app.js
▶ مثال: نقطة دخول التطبيق app.js
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» الخاصة بالتنقل المحلي
<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
<header class="site-header">
<h1><%= title %></h1>
</header>
▶ مثال: ملف تابع تذييل محلي partials/footer.ejs
<footer class="site-footer">
<p>© 2026 My Blog. Powered by Express + EJS.</p>
</footer>
▶ مثال: عدد مشاهدات صفحة قائمة المقالات/index.ejs
<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
<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="/">← Back to all posts</a>
كيف يبدو الشكل بعد تشغيل التطبيق:
http://localhost:3000 → Article List Page
http://localhost:3000/post/1 → Article Details Page
http://localhost:3000/post/2 → Second Article
❓ أسئلة شائعة
express-ejs-layouts لتنفيذ وراثة التخطيطات، أو يمكنك استيراد العروض الجزئية يدويًّا باستخدام <%- include() %>؛ أما Pug فيدعم بشكل أصلي extends + block لتنفيذ وراثة التخطيطات.res.render؟res.render('index', {title: 'Hi'})؛ وفي القالب، يمكنك كتابة <%= title %>.livereload لتمكين التحديث التلقائي للمتصفح، مما يغنيك عن الحاجة إلى تحديث الصفحة يدويًّا.<%= يعرض القيم بعد تهيئة HTML لمنع هجمات XSS؛ وهذا هو الخيار الآمن. <%- يعرض المحتوى الخام دون تهيئة، ويجب استخدامه فقط عندما يكون المحتوى موثوقًا به أو عند تضمين قوالب فرعية.async/await في القوالب؟await على المستوى الأعلى في قوالب EJS. يجب عليك إتمام جميع عمليات استرداد البيانات غير المتزامنة في المسار، ثم تمرير النتائج إلى القالب باستخدام res.render().📖 ملخص
- القصة الأولى: المفاهيم الأساسية وكيفية استخدام واجهة أداة «تشارلي» الإدارية
- 2 المفاهيم الأساسية واستخدامات عملية عرض القوالب
- 3 المفاهيم الأساسية واستخدامات قواعد بناء الجملة الأساسية في EJS
- 4 مفاهيم أساسية واستخدامات تكوين Express + EJS
- 5 مفاهيم أساسية واستخدامات قواعد لغة Pug الأساسية
- 6 المفاهيم الأساسية واستخدامات «الطرق الجزئية» وإعادة استخدام التخطيطات
- 7 مفاهيم أساسية وطرق استخدام لمقارنة محركات القوالب
- 8 مفاهيم أساسية وطرق الاستخدام في المقارنة بين المسؤولية الاجتماعية للشركات (SSR) والمسؤولية الاجتماعية للشركات (CSR)
📝 تمارين
- أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
- قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
- راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
- التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
- حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.