موقعك بطيء؟ حان وقت تنميط JavaScript
\nهل شعرت يومًا بهذا الألم: الموقع يتحمّل، والرسوم المتحركة سلسة، ولكن بمجرد أن يبدأ المستخدم في التفاعل النشط مع الصفحة — يتجمد كل شيء. الأزرار لا تستجيب، والتمرير متقطع، ووحدة التحكم صامتة. هل هذا مألوف؟ على الأرجح، المشكلة تكمن في أداء JavaScript. هذه اللغة هي روح الويب الحديث، ولكن بدون التحسين المناسب، يمكنها تحويل تحفتك الفنية إلى وحش بطيء.
\nفي هذه المقالة، لن نلقي محاضرات مملة. سنأخذ كودًا حقيقيًا، ونحلله بالكامل باستخدام التنميط، ونوضح لك كيفية استخراج أقصى أداء من كل دورة. هل أنت مستعد لتحويل موقعك إلى سيارة سباق فورمولا 1؟ هيا بنا!
\n\nلماذا يبطئ JavaScript: جذر المشكلة
\nقبل أن نمسك بالأدوات، دعنا نفهم ما الذي يسرق الأداء بالضبط. غالبًا ما يكون هناك ثلاثة أسباب:
\n- \n
- حظر الخيط الرئيسي — يعمل JavaScript في خيط واحد (Event Loop). إذا قمت بتشغيل عملية متزامنة ثقيلة، ستتجمد الواجهة حتى اكتمالها. \n
- تسرب الذاكرة — معالجات الأحداث المنسية، المراجع المغلقة، والمتغيرات العامة. تزداد الذاكرة، ويبطئ الموقع، ثم ينهار.
- الخوارزميات غير الفعالة — حلقات مزدوجة على مصفوفات ضخمة، والتلاعب المتكرر بـ DOM، وهياكل بيانات غير مثالية.
ولكن يكفي نظرية. دعنا نرى كيف يبدو هذا عمليًا.
أدوات التنميط: كاشف الأكاذيب الخاص بك
Chrome DevTools — سكين الجيش السويسري للمنمط
افتح أي صفحة، اضغط F12 وانتقل إلى علامة التبويب Performance. اضغط على زر التسجيل، وقم بتنفيذ إجراء على الصفحة (مثل النقر على زر)، ثم أوقف التسجيل. سترى جدولًا زمنيًا، حيث يتم تفصيل كل ثانية بإطارات. الأشرطة الحمراء هي أعداؤك. تعني أن المتصفح لم يتمكن من عرض الإطار خلال 16.6 مللي ثانية (60 إطارًا في الثانية).
// مثال: دالة بسيطة ولكنها مشكلة
function heavyCalculation() {
let result = 0;
for (let i = 0; i < 100000000; i++) {
result += Math.sqrt(i) * Math.sin(i);
}
return result;
}
document.getElementById('btn').addEventListener('click', () => {
const start = performance.now();
heavyCalculation(); // ستتجمد الواجهة لمدة 2-3 ثوانٍ!
console.log('الوقت:', performance.now() - start);
});
قم بتشغيل التنميط وانقر على الزر. سترى أن الخيط الرئيسي مشغول باستدعاء heavyCalculation لمدة ثانية كاملة. هذا هو عنق الزجاجة.
Memory — صيد تسرب الذاكرة
انتقل إلى علامة التبويب Memory والتقط لقطة للكومة. ثم قم بتنفيذ إجراء على الصفحة عدة مرات والتقط لقطة ثانية. قارن بينهما. إذا كان عدد الكائنات من نفس النوع يتزايد دون توقف — فلديك تسرب.
// تسرب كلاسيكي: معالج منسي
function createButton() {
const btn = document.createElement('button');
btn.textContent = 'اضغط علي';
document.body.appendChild(btn);
// المعالج يحتفظ بمرجع إلى btn، حتى لو تم حذف btn
btn.addEventListener('click', function onClick() {
console.log('تم النقر');
});
// نحذف الزر، لكن المعالج يبقى في الذاكرة
document.body.removeChild(btn);
}
تقنيات التحسين: من النظرية إلى التطبيق
1. تخفيف الحمل عن الخيط الرئيسي باستخدام async/await
إذا كانت لديك مهمة ثقيلة، لا تحظر الخيط. استخدم Web Workers أو قسم المهمة إلى مهام صغيرة. لكن الطريقة الأسهل هي استخدام setTimeout بتأخير صفري أو requestIdleCallback.
// سيء: تحميل متزامن
function loadBigData() {
const data = [];
for (let i = 0; i < 1000000; i++) {
data.push({ id: i, value: Math.random() });
}
render(data);