مرجع سريع

ورقة غش Layout Thrashing وإعادة التدفق القسرية

دليل مرجعي شامل حول Layout Thrashing وإعادة التدفق القسرية (Forced Reflow): خصائص DOM وأحداث الفأرة ودوال window واستدعاءات jQuery التي تبطئ التصيير.

· تاريخ النشر

يحدث ارتعاش التخطيط (Layout thrashing، المعروف أيضاً بإعادة التدفق القسرية المتزامنة) عندما يتناوب كود JavaScript بشكل متكرر بين الكتابة في DOM وقراءة خصائص الأبعاد الهندسية، مما يجبر المتصفح على إعادة حساب تخطيط الصفحة بشكل متزامن.

محفزات إبطال التخطيط (Layout Invalidation)

تجبر خصائص ودوال DOM التالية المتصفح على إعادة حساب الأبعاد الهندسية فوراً متى ما أُبطل التخطيط بتعديلات DOM سابقة.

خصائص ودوال العنصر

أبعاد الصندوق الهندسية

  • clientHeight, clientWidth
  • clientLeft, clientTop
  • offsetHeight, offsetWidth
  • offsetLeft, offsetTop
  • offsetParent
  • scrollHeight, scrollWidth
  • scrollLeft, scrollTop

استعلام الصناديق المحيطة

  • getBoundingClientRect()
  • getClientRects()

إجراءات التمرير

  • scrollIntoView()
  • scrollIntoViewIfNeeded()
  • scrollByLines()
  • scrollByPages()

النص والتركيز

  • innerText
  • outerText
  • focus() (يجبر إعادة حساب حلقة التركيز والتمرير)

خصائص حدث الفأرة

قد يؤدي طلب إحداثيات معينة على كائن MouseEvent إلى تشغيل حساب التخطيط في بعض محركات المتصفحات:

  • layerX, layerY
  • offsetX, offsetY

دوال وخصائص Window

  • window.getComputedStyle() (عند استعلام قيم تعتمد على التخطيط مثل العرض أو الارتفاع أو الهوامش)
  • window.scrollBy()
  • window.scrollTo()
  • window.scrollX, window.scrollY

الإطار والمستند والصورة

  • استعلام width أو height على صورة قبل استقرار التخطيط
  • الوصول إلى document.body.scrollHeight

عمليات jQuery

تقرأ العديد من دوال jQuery المساعدة قيم التخطيط المحسوبة داخلياً:

// تُطلق إعادة التدفق (Reflow)
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width');  // أي حساب نمط يعتمد على التخطيط
$(elem).text();

// محددات زائفة تفحص الرؤية الهندسية
$(':hidden');
$(':visible');
$(':contains');

أفضل الممارسات لمنع Layout Thrashing

1. تجميع القراءات والكتابات (Batch Reads & Writes)

نفّذ جميع قياسات DOM أولاً، ثم اجمع جميع تعديلات DOM معاً.

// سيء: القراءة والكتابة المتناوبة تجبر إعادة حساب التخطيط في كل دورة
elements.forEach((el) => {
  const width = el.clientWidth; // قراءة (تجبر التخطيط)
  el.style.width = width * 2 + 'px'; // كتابة (تبطل التخطيط)
});

// جيد: اجمع القراءات أولاً، ثم اجمع الكتابات
const widths = elements.map((el) => el.clientWidth); // قراءة مجمعة
elements.forEach((el, index) => {
  el.style.width = widths[index] * 2 + 'px'; // كتابة مجمعة
});

2. استخدام requestAnimationFrame

جدوِل كتابات الأنماط في إطار الرسوم المتحركة التالي ليتمكن المتصفح من تحسين دورة التصيير:

const height = element.clientHeight; // قراءة خلال الطور الحالي

window.requestAnimationFrame(() => {
  element.style.height = height + 50 + 'px'; // كتابة في الإطار التالي
});

3. استخدام CSS Transforms و Opacity

فضّل تحريك خصائص CSS التي يعالجها خيط التركيب (transform، opacity) بدلاً من خصائص التخطيط (top، left، width، height).


المراجع

موضوعات ذات صلة