0%
تحسين الأداء في JavaScript

تحسين الأداء في JavaScript

تقنيات وأفضل الممارسات لإنشاء تطبيقات JavaScript سريعة وفعّالة.

I

InSkillCoach

· دقيقة

تحسين الأداء في JavaScript

يُعدّ الأداء جانبًا حاسمًا في تطوير الويب الحديث. فالتطبيقات البطيئة تُحبط المستخدمين، وقد يكون لها أثر سلبي على معدلات التحويل وعلى ظهور الموقع في محركات البحث وعلى التجربة العامة. يستعرض هذا المقال تقنيات تحسين الأداء في JavaScript، بدءًا من الأساسيات وصولًا إلى الاستراتيجيات المتقدمة.

لماذا يُعدّ الأداء مهمًا؟

  • تجربة المستخدم: يتخلّى المستخدمون عن المواقع البطيئة (يغادر 53% منهم الصفحة التي يستغرق تحميلها أكثر من 3 ثوانٍ)
  • تحسين محركات البحث (SEO): تعتمد Google على السرعة كأحد عوامل التصنيف
  • التحويل: قد تؤدي كل ثانية تأخير إلى خفض معدلات التحويل بنسبة 7%
  • إمكانية الوصول: تستفيد الأجهزة والاتصالات المحدودة الإمكانات استفادة كبيرة من التحسينات
  • التكاليف: يستهلك الكود الفعّال موارد أقل على الخادم

فهم أداء JavaScript

محرك JavaScript

لتحسين الأداء بفعالية، من المفيد فهم طريقة عمل محرك JavaScript:

  1. التحليل (Parsing): تحويل الكود إلى شجرة بناء مجردة (Abstract Syntax Tree - AST)
  2. الترجمة (Compilation): التحويل إلى bytecode ثم إلى شيفرة آلة محسّنة
  3. التنفيذ: تنفيذ الكود مع جمع بيانات تُستخدم في تحسينات لاحقة
  4. جمع المهملات (Garbage Collection): تحرير الذاكرة غير المستخدمة

مقاييس الأداء الرئيسية

  • First Contentful Paint (FCP): أول ظهور للمحتوى
  • Largest Contentful Paint (LCP): ظهور المحتوى الرئيسي
  • First Input Delay (FID): التأخير قبل التفاعل
  • Cumulative Layout Shift (CLS): الثبات البصري
  • Time to Interactive (TTI): اللحظة التي تصبح فيها الصفحة تفاعلية
  • Total Blocking Time (TBT): المدة التي يكون فيها الخيط الرئيسي (main thread) محجوبًا

التحسينات الأساسية

1. تقليل عمليات التعامل مع DOM

DOM بطيء. في كل مرة تعدّل فيها DOM، يتعيّن على المتصفح إعادة حساب الأنماط والتخطيط، وأحيانًا إعادة رسم الشاشة.

// Inefficace : multiples manipulations du DOM
for (let i = 0; i < 1000; i++) {
  document.body.innerHTML += `<div>${i}</div>`;
}

// Optimisé : manipulation unique du DOM
let html = '';
for (let i = 0; i < 1000; i++) {
  html += `<div>${i}</div>`;
}
document.body.innerHTML += html;

// Encore mieux : utiliser DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = i;
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

2. تجنّب إعادة التخطيط وإعادة الرسم

  • Reflow (إعادة التخطيط): إعادة حساب كاملة لهندسة العناصر (مكلفة)
  • Repaint (إعادة الرسم): تحديث بصري دون تغيير في الهندسة (أقل كلفة)
// Inefficace : provoque plusieurs reflows
const element = document.getElementById('monElement');
element.style.width = '100px';
element.style.height = '200px';
element.style.margin = '10px';

// Optimisé : un seul reflow
const element = document.getElementById('monElement');
element.classList.add('nouvelles-dimensions');

// Ou avec style
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';

3. استخدام الأحداث بفعالية

// Inefficace : un gestionnaire par élément
document.querySelectorAll('button').forEach(button => {
  button.addEventListener('click', handleClick);
});

// Optimisé : délégation d'événements
document.addEventListener('click', e => {
  if (e.target.matches('button')) {
    handleClick(e);
  }
});

4. تحسين الحلقات التكرارية

const arr = new Array(1000000).fill(1);

// Inefficace : accès à la longueur à chaque itération
for (let i = 0; i < arr.length; i++) {
  // traitement
}

// Optimisé : stockage de la longueur
const len = arr.length;
for (let i = 0; i < len; i++) {
  // traitement
}

// Moderne : méthodes de tableau
arr.forEach(item => {
  // traitement
});

// Souvent le plus rapide pour les tableaux simples
for (const item of arr) {
  // traitement
}

5. التخزين المؤقت للنتائج (Memoization) للدوال المكلفة

// Sans memoization
function calculComplexe(n) {
  console.log('Calcul pour', n);
  // Simulation d'un calcul coûteux
  return n * n;
}

// Avec memoization
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const calculComplexeMemoize = memoize(calculComplexe);

// Premier appel : calcul effectué
calculComplexeMemoize(42);
// Second appel avec même argument : résultat mis en cache
calculComplexeMemoize(42);

التحسينات المتقدمة

1. Web Workers للمهام المكثفة

تتيح Web Workers تنفيذ كود JavaScript في خيط (thread) منفصل، مما يمنع حجب واجهة المستخدم.

// main.js
const worker = new Worker('worker.js');

worker.onmessage = function(e) {
  console.log('Résultat reçu du worker:', e.data);
};

worker.postMessage({ data: complexData, operation: 'process' });

// worker.js
self.onmessage = function(e) {
  const { data, operation } = e.data;
  
  if (operation === 'process') {
    // Traitement intensif qui ne bloque pas l'UI
    const result = processData(data);
    self.postMessage(result);
  }
};

2. المحاكاة الافتراضية للقوائم الطويلة (Virtualization)

بالنسبة إلى القوائم الطويلة جدًا، اعرض فقط العناصر الظاهرة في نافذة العرض.

// Exemple simplifié de virtualisation
function renderVisibleItems(items, container, itemHeight) {
  const scrollTop = window.scrollY;
  const viewportHeight = window.innerHeight;
  
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    items.length - 1,
    Math.floor((scrollTop + viewportHeight) / itemHeight)
  );
  
  container.style.height = `${items.length * itemHeight}px`;
  container.innerHTML = '';
  
  for (let i = startIndex; i <= endIndex; i++) {
    const item = document.createElement('div');
    item.textContent = items[i];
    item.style.position = 'absolute';
    item.style.top = `${i * itemHeight}px`;
    item.style.height = `${itemHeight}px`;
    container.appendChild(item);
  }
}

// Utilisation avec un écouteur de défilement
window.addEventListener('scroll', () => {
  renderVisibleItems(myLargeArray, myContainer, 50);
});

3. الاستخدام الفعّال للمؤقّتات

// Inefficace : multiples timers
function inefficientAnimation() {
  for (let i = 0; i < 100; i++) {
    setTimeout(() => updateElement(i), i * 10);
  }
}

// Optimisé : requestAnimationFrame
function efficientAnimation() {
  let i = 0;
  function step() {
    updateElement(i);
    i++;
    if (i < 100) {
      requestAnimationFrame(step);
    }
  }
  requestAnimationFrame(step);
}

4. Debounce وThrottle

// Debounce : exécute la fonction après un délai sans appels
function debounce(fn, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), delay);
  };
}

// Throttle : limite le nombre d'appels dans le temps
function throttle(fn, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      fn.apply(this, args);
      lastCall = now;
    }
  };
}

// Utilisation
const debouncedResize = debounce(() => {
  console.log('Redimensionnement terminé');
}, 200);

const throttledScroll = throttle(() => {
  console.log('Défilement contrôlé');
}, 100);

window.addEventListener('resize', debouncedResize);
window.addEventListener('scroll', throttledScroll);

تحسين التحميل

1. تقسيم الكود (Code splitting)

قسّم الكود إلى أجزاء أصغر بحيث لا يُحمَّل إلا ما هو ضروري.

// Chargement dynamique avec import()
button.addEventListener('click', async () => {
  const module = await import('./feature.js');
  module.default();
});

2. التحميل الكسول (Lazy loading)

// Lazy loading d'images
document.querySelectorAll('img[data-src]').forEach(img => {
  const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        img.src = img.dataset.src;
        observer.disconnect();
      }
    });
  });
  observer.observe(img);
});

// Lazy loading de composants
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <React.Suspense fallback={<div>Chargement...</div>}>
      <LazyComponent />
    </React.Suspense>
  );
}

3. Preload وPrefetch وPreconnect

<!-- Preload : ressource critique pour la page actuelle -->
<link rel="preload" href="critical.js" as="script">

<!-- Prefetch : ressource pour la navigation future -->
<link rel="prefetch" href="next-page.js" as="script">

<!-- Preconnect : établir une connexion anticipée -->
<link rel="preconnect" href="https://api.example.com">

قياس الأداء ومراقبته

1. Performance API

// Mesure du temps d'exécution
performance.mark('debut');
// Code à mesurer
performance.mark('fin');
performance.measure('monOperation', 'debut', 'fin');

const mesures = performance.getEntriesByType('measure');
console.log(mesures[0].duration);

// Navigation Timing API
window.addEventListener('load', () => {
  const timing = performance.timing;
  const pageLoadTime = timing.loadEventEnd - timing.navigationStart;
  console.log(`Temps de chargement: ${pageLoadTime}ms`);
});

2. أدوات التطوير

  • Chrome DevTools: تبويبات Performance وNetwork وMemory
  • Lighthouse: تدقيق آلي للأداء
  • WebPageTest: اختبارات أداء مفصّلة
  • Bundle analyzers: تحليل حجم الحزم (bundles)

أفضل الممارسات العامة

  1. تصغير ملفات JavaScript وضغطها
  2. استخدام التخزين المؤقت للمتصفح بفعالية
  3. تجنّب المكتبات غير الضرورية وتفضيل الحلول الأصلية
  4. اعتماد ميزات JavaScript الحديثة (مع التحقق من التوافق)
  5. تحليل أداء الكود بانتظام (Profiling) لتحديد نقاط الاختناق
  6. تحسين الصور والوسائط لتقليل الحمل الإجمالي
  7. استخدام CDN للموارد الثابتة
  8. تطبيق العرض من جهة الخادم (Server-Side Rendering) أو التوليد الثابت متى أمكن

أخطاء شائعة يجب تجنّبها

  1. الإفراط في التحسين المبكر: قِس أولًا ثم حسِّن
  2. التحسينات الدقيقة المفرطة: ركّز على المكاسب الملموسة
  3. تجاهل تجربة الأجهزة المحمولة: اختبر على أجهزة حقيقية واتصالات بطيئة
  4. إهمال إمكانية الوصول: يجب ألا تُضعف التحسينات إمكانية الوصول
  5. نسيان الاختبار بعد التحسين: تحقّق من أن كل شيء لا يزال يعمل بالشكل الصحيح

الخلاصة

يمثّل تحسين أداء JavaScript توازنًا بين السرعة وقابلية الصيانة وتجربة المستخدم. ابدأ بالتحسينات ذات الأثر الكبير، وقِس النتائج، ثم صقل العمل تدريجيًا.

تذكّر أن أفضل تحسين هو في كثير من الأحيان الكود الذي لا تكتبه. فقبل إضافة مكتبة أو ميزة جديدة، قيِّم أثرها على الأداء وهل هي ضرورية فعلًا.

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

InSkillCoach

نبذة عن InSkillCoach

خبير في التدريب والتقنيات

مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.

الشهادات:

  • AWS Certified Solutions Architect – Professional
  • شهادات Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
849
256

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!