Node.js: مقدمة إلى Node.js
أليس مطورة برمجيات «الخلفية». كانت شركتها بحاجة إلى «بناء» واجهة برمجة تطبيقات REST للخلفية لتطبيق جوال في غضون أسبوعين. كان الفريق يتألف بالكامل من مطوري «الواجهة الأمامية» الذين لا يجيدون سوى لغة JavaScript، ولم يكن أحد منهم على استعداد لتخصيص وقت لتعلم لغة برمجة جديدة. حتى أن مدير المشروع فكر في توظيف مطور لغة Python، لكن الوقت لم يكن كافياً ببساطة. وبينما بدأ الفريق يشعر بالقلق، اكتشفت أليس Node.js — وهو بيئة تشغيل تعمل بلغة JavaScript على الخادم. تمكن مطورو الواجهة الأمامية من كتابة واجهات برمجة التطبيقات الخلفية باستخدام الصيغة التي كانوا على دراية بها بالفعل، وتم إطلاق المشروع بنجاح بعد أسبوعين. أدرك أليس من خلال ذلك أن Node.js ليست مجرد أداة — بل هي جسر يربط بين الواجهة الأمامية والواجهة الخلفية.
ستتعلم:
- تعريف Node.js والغرض الأساسي منه
- البنية الأساسية لمحرك V8 و Node.js
- كيفية عمل عمليات الإدخال/الإخراج (I/O) التي تعتمد على الأحداث وغير المعطلة
- الاختلافات الرئيسية بين Node.js و JavaScript في المتصفح
- سياسات إصدارات Node.js وحالات الاستخدام النموذجية
1. ما هو Node.js؟
(1) التعريف
Node.js هي بيئة تشغيل لـ JavaScript تستند إلى محرك Chrome V8. وهي تتيح تشغيل JavaScript مباشرةً على الخادم، بشكل مستقل عن المتصفح. Node.js ليست لغة برمجة جديدة ولا إطار عمل، بل هي منصة لتنفيذ JavaScript من جانب الخادم.
(2) خلفية نشأتها
في عام 2009، أنشأ رايان دال Node.js استنادًا إلى محرك V8. وكان هدفه الأصلي هو حل مشكلة الحجب الناتجة عن عمليات الإدخال/الإخراج من جانب الخادم — حيث تقوم خوادم الويب التقليدية بحجب الخيوط عند معالجة عمليات الإدخال/الإخراج مثل قراءة الملفات وطلبات الشبكة، في حين يستخدم Node.js نهجًا قائمًا على الأحداث، مما يسمح لخيط واحد بمعالجة عدد كبير من الاتصالات المتزامنة بكفاءة.
▶ مثال: أول برنامج نصي لك بلغة Node.js
إنشاء الملف hello.js:
console.log('Hello, Node.js!');
console.log('Node.js version:', process.version);
console.log('Platform:', process.platform);
قم بتشغيل الأمر التالي في المحطة الطرفية:
node hello.js
النتيجة المتوقعة:
Hello, Node.js!
Node.js version: v20.11.0
Platform: win32
2. محرك V8 وبنية Node.js
(1) دور محرك V8
V8 هو محرك لغة جافا سكريبت طورته شركة جوجل، ويقوم بتحويل كود جافا سكريبت إلى كود آلي لتنفيذه. ويأتي محرك V8 مدمجًا بشكل مباشر في Node.js، وهذا هو السبب وراء الأداء العالي الذي يقدمه. وتتيح تقنية التحويل «في الوقت المناسب» (JIT) الخاصة بـ V8 تشغيل جافا سكريبت بسرعات تقترب من سرعات الكود الأصلي.
(2) طبقات بنية Node.js
تنقسم بنية Node.js إلى أربع طبقات من الأسفل إلى الأعلى:
| المستوى | التكوين | الوظيفة |
|---|---|---|
| طبقة كود جافا سكريبت | ملفات .js التي يكتبها المستخدم | منطق الأعمال |
| طبقة واجهة برمجة تطبيقات Node.js | وحدات مثل fs وhttp وpath | توفر إمكانيات من جانب الخادم |
| طبقة ربط Node.js | كود الجسر بلغة C++ | ربط JavaScript بمكتبة C الأساسية |
| المكتبات ذات المستوى المنخفض | V8، libuv، OpenSSL، وغيرها | المحركات ونداءات النظام |
(3) الدور الرئيسي لمكتبة libuv
libuv هي مكتبة الإدخال/الإخراج غير المتزامن لـ Node.js، وتوفر حلقة أحداث، ومجموعة خيوط، وتجريدًا متعدد المنصات لعمليات الإدخال/الإخراج غير المتزامنة. وهي تمكّن Node.js من معالجة العمليات غير المتزامنة — مثل عمليات الإدخال/الإخراج للملفات، وعمليات الشبكة، والمؤقتات — بطريقة متسقة عبر أنظمة التشغيل المختلفة.
▶ مثال: عرض معلومات التبعيات في Node.js
console.log('V8 version:', process.versions.v8);
console.log('libuv version:', process.versions.libuv);
console.log('OpenSSL version:', process.versions.openssl);
console.log('Node.js version:', process.version);
الناتج:
V8 version: 11.3.244.8
libuv version: 1.46.0
OpenSSL version: 3.0.12
Node.js version: v20.11.0
▶ مثال:(4) مخطط الهندسة المعمارية
graph TB
A["JavaScript Code<br/>(User-written .js Documents)"] --> B["Node.js API<br/>(fs / http / path / ...)"]
B --> C["Node.js Bindings<br/>(C++ Bridge Layer)"]
C --> D1["V8 Engine<br/>(JavaScript Compile and Run)"]
C --> D2["libuv<br/>(Event Loop + Asynchronous I/O)"]
C --> D3["Other C Libs<br/>(OpenSSL / zlib / c-ares)"]
D2 --> E["Operating System<br/>(Documents / Internet / Process)"]
3. عمليات الإدخال/الإخراج (I/O) التي تعمل استجابةً للأحداث وغير المعطلة
(1) النموذج القائم على الأحداث
يستخدم Node.js بنية قائمة على الأحداث: حيث يستجيب البرنامج للعمليات المختلفة عن طريق تسجيل دالات الاستدعاء المرتبطة بالأحداث، بدلاً من إجراء عمليات الاستقصاء بشكل نشط. وعند اكتمال عملية الإدخال/الإخراج، تضع حلقة الأحداث دالة الاستدعاء المقابلة في قائمة انتظار حتى يحين وقت تنفيذها.
(2) معنى الإدخال/الإخراج غير المعطل
تقوم الخوادم التقليدية بحظر مؤشر الترابط الحالي أثناء قراءة الملف حتى يتم قراءة الملف بالكامل. أما Node.js فتعود فور بدء طلب الإدخال/الإخراج، وتواصل تنفيذ التعليمات البرمجية التالية، ثم تعالج النتيجة عبر استدعاء مرتد بمجرد اكتمال عملية الإدخال/الإخراج. ويتيح هذا النهج لمؤشر ترابط واحد معالجة مئات الطلبات المتزامنة في آن واحد.
(3) مقدمة إلى حلقة الأحداث
تُعد حلقة الأحداث آلية الجدولة الأساسية في Node.js، والتي تعمل في حلقة متكررة تمر بالمراحل التالية:
- الساعات: تنفيذ دالة الاستدعاء setTimeout / setInterval
- عمليات الاستدعاء المعلقة: عمليات الاستدعاء التي تُنفِّذ عمليات نظامية
- الوضع الخامل، الاستعداد: للاستخدام الداخلي
- poll: يسترد أحداث الإدخال/الإخراج الجديدة وينفذ إجراءات الاستدعاء المرتبطة بالإدخال/الإخراج
- تحقق: تنفيذ دالة الاستدعاء setImmediate
- دوال الاستدعاء عند الإغلاق: تنفيذ دوال الاستدعاء الخاصة بأحداث الإغلاق
▶ مثال: قراءة الملفات بطريقة «الحجب» مقابل «عدم الحجب»
وضع الحجب (غير موصى به):
const fs = require('fs');
const data = fs.readFileSync('/etc/hosts', 'utf8');
console.log('File read (blocking):', data.length, 'bytes');
console.log('This line runs after file is read');
الوضع غير المعطل (موصى به):
const fs = require('fs');
fs.readFile('/etc/hosts', 'utf8', (err, data) => {
if (err) throw err;
console.log('File read (non-blocking):', data.length, 'bytes');
});
console.log('This line runs immediately');
الإخراج (الوضع غير المعطل):
This line runs immediately
File read (non-blocking): 250 bytes
لاحظ أنه في الوضع غير المعطل، تُطبع العبارة «يتم تنفيذ هذا السطر على الفور» أولاً، مما يشير إلى أن readFile لم يوقف تنفيذ الكود اللاحق.
4. Node.js مقابل جافا سكريبت في المتصفح
(1) الاختلافات الرئيسية
يستخدم كل من Node.js وJavaScript في المتصفح نفس مواصفات اللغة (ECMAScript)، لكن بيئات التشغيل الخاصة بهما وواجهات برمجة التطبيقات (API) المتاحة تختلف تمامًا.
| معايير المقارنة | Node.js | جافا سكريبت في المتصفح |
|---|---|---|
| بيئة التشغيل | الخادم | المتصفح |
| نظام الوحدات | CommonJS / ESM | ESM |
| كائن عام | عام / عملية | نافذة / مستند |
| نظام الملفات | الوصول المباشر عبر وحدة fs | لا يمكن الوصول إليه مباشرة |
| إدخال/إخراج الشبكة | وحدات http / net | fetch / XMLHttpRequest |
| معالجة DOM | بدون DOM | واجهة برمجة تطبيقات DOM |
| مدير الحزم | npm | لا شيء (أو عبر CDN) |
| الاستخدامات الرئيسية | تطبيقات الخادم، أدوات واجهة الأوامر (CLI) | التفاعلات عبر الويب، عرض واجهة المستخدم |
(2) أوجه التشابه
- جميعها تتوافق مع معيار ECMAScript
- كلاهما يستخدم محرك V8 (Chrome و Node.js)
- كلاهما يدعمان Promise و async-await
- جميعها تحتوي على آلية حلقة الأحداث
▶ مثال: نفس الصيغة، واجهات برمجة تطبيقات مختلفة
كود المتصفح:
document.getElementById('output').textContent = 'Hello from browser';
كود Node.js:
const fs = require('fs');
fs.writeFileSync('output.txt', 'Hello from Node.js');
تبقى صيغة لغة جافا سكريبت كما هي، لكن الكائنات التي يتم التعامل معها مختلفة تمامًا: فالمتصفحات تتعامل مع نموذج DOM، بينما يتعامل Node.js مع نظام الملفات.
5. حالات الاستخدام النموذجية لـ Node.js
(1) خدمة واجهة برمجة التطبيقات REST
يُعد Node.js خيارًا مناسبًا تمامًا لإنشاء واجهات برمجة التطبيقات (API) التي تعمل وفقًا لمعيار REST. ونظرًا لأن نموذج الإدخال/الإخراج (I/O) غير المعطل الخاص به يتعامل بكفاءة مع العديد من الطلبات المتزامنة، ولأن JSON هو تنسيق البيانات الأصلي في JavaScript، فإن ذلك يزيل العبء الإضافي الناجم عن التسلسل.
(2) أدوات واجهة سطر الأوامر
تعد Webpack وBabel وESLint وnpm أدوات CLI تم إنشاؤها باستخدام Node.js. ويوفر Node.js مجموعة غنية من واجهات برمجة التطبيقات (APIs) للتعامل مع نظام الملفات، والعمليات الفرعية، ووسائط سطر الأوامر.
(3) التطبيقات التي تعمل في الوقت الفعلي
تتطلب سيناريوهات مثل أنظمة الدردشة، والتعاون عبر الإنترنت، والإشعارات في الوقت الفعلي، اتصالات مستمرة وثنائية الاتجاه. ويُعد دعم WebSocket في Node.js وبنيته القائمة على الأحداث مناسبين بشكل طبيعي لتلبية هذه الاحتياجات.
(4) الخدمات الصغيرة
يتميز Node.js بسرعة التشغيل واستهلاكه القليل للذاكرة، مما يجعله خيارًا مثاليًّا للخدمات الصغيرة خفيفة الوزن. ويمكن نشر كل خدمة صغيرة وتوسيع نطاقها بشكل مستقل.
(5) الزحف على الويب وجمع البيانات
عند دمج Node.js مع مكتبات مثل Cheerio، يمكنه الزحف إلى بيانات الويب وتحليلها بكفاءة، كما أن عمليات الإدخال/الإخراج غير المتزامنة تجعل الطلبات المتزامنة فعالة للغاية.
▶ مثال: مقارنة بين حالات الاستخدام
| السيناريو | المزايا | المشاريع النموذجية |
|---|---|---|
| واجهة برمجة التطبيقات REST | عمليات الإدخال/الإخراج غير المعطلة + JSON الأصلي | Express / Fastify / Koa |
| أدوات CLI | عمليات الملفات + العمليات الفرعية | Webpack / Babel / ESLint |
| التطبيقات في الوقت الفعلي | WebSocket + قائمة على الأحداث | Socket.IO / ws |
| الخدمات الصغيرة | خفيفة الوزن + سريعة الإعداد | NestJS / Seneca |
| استخراج البيانات من الويب | التزامن غير المتزامن + تحليل DOM | Puppeteer / Cheerio |
▶ مثال: إنشاء خادم HTTP في 5 أسطر من التعليمات البرمجية
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello from Node.js API' }));
});
server.listen(3000, () => console.log('Server running on port 3000'));
node server.js
Server running on port 3000
تفضل بزيارة http://localhost:3000 للاطلاع على استجابة JSON.
6. سياسة إصدارات Node.js
(1) LTS والإصدار الحالي
يستخدم Node.js استراتيجية إصدار ثنائية المسار:
| المقارنة | LTS (الدعم طويل الأمد) | الإصدار الحالي |
|---|---|---|
| دورة الإصدار | إصدار رئيسي واحد كل سنتين | إصدار رئيسي واحد كل 6 أشهر |
| فترة الصيانة | 30 شهراً | حوالي 6 أشهر |
| الاستقرار | عالي؛ لا يتضمن سوى إصلاحات الأخطاء والتحديثات الأمنية | منخفض؛ يتضمن أحدث الميزات |
| حالات الاستخدام | بيئة الإنتاج | تجربة الميزات الجديدة، والاختبار |
| قواعد ترقيم الإصدارات | الأرقام الزوجية (18، 20، 22...) | الأرقام الفردية (19، 21، 23...) |
(2) توصيات بشأن اختيار الإصدار
- مشاريع الإنتاج: استخدم دائمًا الإصدار النشط من LTS
- مرحلة التعلم: نوصي باستخدام Active LTS، حيث إنه يوفر الموارد والنظام البيئي الأكثر شمولاً.
- ميزة تجريبية: يمكن استخدام الإصدار الحالي، لكن لا يُنصح باستخدامه في بيئة الإنتاج.
(3) أدوات التحكم في الإصدارات
نوصي باستخدام nvm (مدير إصدارات Node) لإدارة إصدارات Node.js والتبديل بينها.
▶ مثال: إدارة الإصدارات باستخدام nvm
nvm install 20
nvm use 20
nvm ls
v18.17.0
-> v20.11.0
system
default -> 20 (-> v20.11.0)
▶ مثال: التحقق من إصدار Node.js الحالي وحالته
node -v
node -e "console.log(process.release.lts ? 'LTS: ' + process.release.lts : 'Current (non-LTS)')"
v20.11.0
LTS: Iron
❓ أسئلة شائعة
package.json أو node_modules، ويتضمن أدوات تنسيق واختبار مدمجة في مكتبته القياسية. لا يزال نظام Deno البيئي في طور النمو، في حين أن نظام Node.js البيئي أكثر نضجًا.node -v في نافذة الأوامر للتحقق من أنه يعمل.📖 ملخص
- Node.js هي بيئة تشغيل لـ JavaScript من جانب الخادم تعتمد على محرك V8؛ وهي ليست لغة برمجة جديدة.
- تتكون البنية من أربع طبقات: كود JavaScript → واجهة برمجة تطبيقات Node.js → روابط → المكتبات الأساسية (V8 / libuv)
- الإدخال/الإخراج (I/O) القائم على الأحداث وغير المعطل هو الآلية الأساسية لتحقيق التزامن العالي في Node.js
- يستخدم Node.js نفس صيغة لغة JavaScript المستخدمة في المتصفحات، لكن واجهات برمجة التطبيقات (APIs) وبيئات التشغيل الخاصة به مختلفة تمامًا.
- حالات الاستخدام النموذجية: واجهات برمجة التطبيقات (REST APIs)، وأدوات واجهة سطر الأوامر (CLI)، والتطبيقات التي تعمل في الوقت الفعلي، والخدمات الصغيرة، وبرامج الزحف على الويب
- احرص على استخدام الإصدار LTS في بيئات الإنتاج، وقم بإدارة الإصدارات المتعددة باستخدام nvm.
📝 تمارين
- قم بتثبيت Node.js (يُنصح باستخدام الإصدار LTS)، ثم قم بتشغيل الأمر
node -vللتحقق من نجاح التثبيت، وقم بتدوين رقم الإصدار الخاص بك وما إذا كان إصدار LTS أم لا. - أنشئ ملفًا باسم
hello.js، واستخدمconsole.logلعرض اسمك ورقم إصدار Node.js، ثم قم بتشغيله، والتقط لقطة شاشة - اكتب برنامجًا نصيًّا يستخدم الكائن
processلعرض المعلومات التالية: إصدار Node.js، ومنصة نظام التشغيل، ودليل العمل الحالي، وبنية وحدة المعالجة المركزية. - استخدم
fs.readFileلقراءة ملف نصي في الوضع غير المعطل وعرض محتوياته، ثم لاحظ ترتيب تنفيذ دوال الاستدعاء - قارن بين
fs.readFileSync(حجب) وfs.readFile(غير حجب) عن طريق طباعة الطوابع الزمنية قبل وبعد استدعاء الدالة المرتدة، على التوالي، أثناء قراءة الملف نفسه، لتوضيح الفرق في طريقة تنفيذهما.
7. مثال شامل: برنامج نصي لقراءة معلومات النظام
يستخدم البرنامج النصي التالي الوحدات المدمجة في Node.js لقراءة معلومات النظام وعرضها، مما يوضح القدرات الأساسية لـ Node.js كبيئة تشغيل من جانب الخادم:
const os = require('os');
const fs = require('fs');
const path = require('path');
const sysInfo = {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
hostname: os.hostname(),
totalMemoryMB: Math.round(os.totalmem() / 1024 / 1024),
freeMemoryMB: Math.round(os.freemem() / 1024 / 1024),
cpuCores: os.cpus().length,
cpuModel: os.cpus()[0].model,
uptimeHours: (os.uptime() / 3600).toFixed(2),
cwd: process.cwd(),
homedir: os.homedir()
};
const output = JSON.stringify(sysInfo, null, 2);
console.log(output);
const filePath = path.join(os.homedir(), 'nodejs-sysinfo.txt');
fs.writeFile(filePath, output, 'utf8', (err) => {
if (err) {
console.error('Write failed:', err.message);
return;
}
console.log('System info saved to:', filePath);
});
النتيجة المتوقعة:
{
"nodeVersion": "v20.11.0",
"platform": "win32",
"arch": "x64",
"hostname": "DESKTOP-ABC123",
"totalMemoryMB": 16384,
"freeMemoryMB": 8192,
"cpuCores": 8,
"cpuModel": "Intel(R) Core(TM) i7-12700K",
"uptimeHours": "48.32",
"cwd": "C:\\projects\\node-demo",
"homedir": "C:\\Users\\Alice"
}
System info saved to: C:\Users\Alice\nodejs-sysinfo.txt
يوضح هذا البرنامج النصي ثلاث قدرات أساسية لـ Node.js: استرداد معلومات نظام التشغيل (os)، والكتابة إلى ملف (fs)، ومعالجة المسارات (path). كما يستخدم البرنامج الإدخال/الإخراج غير المعطل (non-blocking I/O) للكتابة إلى ملف — حيث يتم تنفيذ استدعاء الرد (callback) الخاص بـ fs.writeFile بعد console.log، مما يوضح الطبيعة القائمة على الأحداث لـ Node.js.