تحسين أداء تطبيقات JavaScript للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا يعتبر أداء JavaScript مهمًا؟

\n

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

\n

سننظر في ثلاثة مجالات رئيسية: البرمجة غير المتزامنة، إدارة الذاكرة، والتنميط. جميع الأمثلة ستكون على Node.js.

\n\n

1. البرمجة غير المتزامنة: تجنب حظر Event Loop

\n

يعمل Node.js في وضع أحادي الخيط باستخدام Event Loop. إذا قمت بتنفيذ عملية ثقيلة متزامنة (مثل قراءة ملف كبير أو حلقة معقدة)، فإن الخادم بأكمله "يتجمد" حتى اكتمالها. لتجنب ذلك، استخدم الطرق غير المتزامنة.

\n

مثال سيء (كود حظر):

\n
const fs = require('fs');\nconst data = fs.readFileSync('/bigfile.txt', 'utf8'); // يحظر التدفق\nconsole.log('تم قراءة الملف');\n
\n

مثال جيد (غير حظر):

\n
const fs = require('fs').promises;\nasync function readFile() {\n  const data = await fs.readFile('/bigfile.txt', 'utf8');\n  console.log('تم قراءة الملف');\n}\nreadFile();\n
\n

استخدم async/await والوعود (promises) لجميع عمليات الإدخال/الإخراج (I/O). هذا يسمح لـ Event Loop بمعالجة الطلبات الأخرى أثناء انتظار الرد من قاعدة البيانات أو نظام الملفات.

\n

نصيحة: تجنب الطرق المتزامنة في بيئة الإنتاج

\n

طرق مثل fs.readFileSync أو crypto.pbkdf2Sync مسموح بها فقط عند بدء التطبيق. في وقت التشغيل، استخدم إصداراتها غير المتزامنة.

\n\n

2. تحسين إدارة الذاكرة ومنع التسريبات

\n

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

\n

الأسباب الرئيسية للتسريبات:

\n
    \n
  • المتغيرات العامة
  • \n
  • المؤقتات غير المنظفة (setInterval بدون clearInterval)
  • \n
  • المراجع المغلقة في الإغلاقات (closures)
  • \n
  • مستمعو الأحداث الذين لا يتم إزالتهم
  • \n
\n

مثال على تسريب عبر المستمعين:

\n
const EventEmitter = require('events');\nconst emitter = new EventEmitter();\n\nfunction processRequest() {\n  emitter.on('data', (chunk) => {\n    // معالجة البيانات\n  });\n}\n// كل استدعاء لـ processRequest يضيف مستمعًا جديدًا، لكنه لا يزيل القديم!\n
\n

الحل: قم بإزالة المستمعين أو استخدم once

\n
emitter.once('data', (chunk) => {\n  // سيتم تنفيذه مرة واحدة فقط وسيتم إزالته تلقائيًا\n});\n
\n

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

\n\n

3. التخزين المؤقت والتنميط

\n

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

\n

تخزين مؤقت بسيط باستخدام Map:

\n
const cache = new Map();\n\nfunction expensiveCalculation(input) {\n  if (cache.has(input)) {\n    console.log('من التخزين المؤقت');\n    return cache.get(input);\n  }\n  const result = input * 1000 + Math.random(); // محاكاة منطق معقد\n  cache.set(input, result);\n  return result;\n}\n\nconsole.log(expensiveCalculation(5)); // حساب\nconsole.log(expensiveCalculation(5)); // من التخزين المؤقت\n
\n

للسيناريوهات الأكثر تعقيدًا، استخدم مكتبات مثل node-cache أو Redis.

\n

التنميط باستخدام الأداة المدمجة في Node.js

\n

قم بتشغيل التطبيق مع العلم --prof:

\n
node --prof app.js\n
\n

بعد إيقاف التطبيق، سيظهر ملف isolate-*.log في المجلد. قم بتحويله إلى

المدونات

توصيات الكتب