هذا المقال يتطلب مستوى: متوسط
List Virtualization: اعرض 10,000 صف بدون ما المتصفح يتجمّد
لو عندك جدول أو قائمة فيها آلاف الصفوف والتمرير بيتقطّع، انت مش محتاج جهاز أقوى. المشكلة إنك بترسم كل العناصر في الـ DOM دفعة واحدة. الـ List Virtualization بيرسم اللي ظاهر قدام المستخدم بس، فيرجّع التمرير سلس عند 60 إطار في الثانية.
المشكلة باختصار
تخيّل صفحة بتعرض 10,000 منتج أو 10,000 صف لوج. المتصفح بيبني 10,000 عنصر DOM، وكل عنصر له تكلفة في الذاكرة وفي حساب التخطيط (layout). النتيجة: التمرير بيتهنّج، والكتابة في أي input بتتأخر. ده اللي بيحصل فعلاً لمّا الـ DOM يكبر أكتر من اللازم.
المفهوم الأساسي: نافذة القطار
قبل التعريف العلمي، خد المثال ده. انت راكب في قطار وبتبصّ من الشباك. المنظر اللي برّه طوله 1000 كيلومتر، بس الشباك بيوريك 50 متر بس في كل لحظة. مش معقول تشوف الـ 1000 كيلومتر كلهم مرة واحدة، ومش محتاج. وانت ماشي، الشباك بيوريك الجزء الجديد ويخفي اللي عدّى.
الـ Virtualization بيشتغل بنفس الفكرة بالظبط. القائمة كلها 10,000 صف، بس "الشباك" (منطقة العرض الظاهرة) بتوري حوالي 20 صف. المكتبة بترسم الـ 20 دول بس، ووانت بتعمل scroll بتستبدلهم بصفوف جديدة بدل ما تبني الـ 10,000 من الأول.
التعريف العلمي: الـ List Virtualization (أو Windowing) تقنية بترسم في الـ DOM المجموعة الفرعية الظاهرة من العناصر فقط، مع buffer صغير، وتحدّث محتوى هذه العناصر مع التمرير بدل إنشاء عقدة DOM لكل عنصر. النتيجة عدد ثابت من عقد الـ DOM مهما كبر طول القائمة.
ليه القائمة الكبيرة بتجمّد المتصفح
الـ main thread في المتصفح بيعمل JavaScript وحساب الأنماط والتخطيط والرسم، كله على خيط واحد. عند 60fps عندك 16.66 مللي ثانية لكل إطار عشان تخلّص كل ده. أي مهمة بتاخد أكتر من 50 مللي ثانية بتتصنّف Long Task، وده اللي بيبان للمستخدم على شكل تقطيع (jank) وتأخّر في الاستجابة.
لمّا ترسم 10,000 صف، كل تغيير في القائمة بيجبر المتصفح يعيد حساب تخطيط آلاف العناصر. الافتراض هنا إن عندك قائمة فعلاً كبيرة (أكتر من 50–100 عنصر متشابه)؛ تحت كده المشكلة مش موجودة أصلاً.
الحل: react-window في 4 خطوات
- ثبّت المكتبة:
npm install react-window. - استخدم FixedSizeList لو كل الصفوف ليها نفس الارتفاع — ده الأسرع لأن المكتبة مش محتاجة تحسب موضع كل صف.
- اعمل component للصف الواحد، وطبّق عليه الـ
styleاللي بتبعته المكتبة (مهم جداً للتموضع الصحيح). - اضبط
overscanCountلرسم صفوف إضافية فوق وتحت النافذة عشان تمنع وميض المساحة الفاضية وقت السحب السريع.
import { FixedSizeList } from 'react-window';
function Row({ index, style }) {
// لازم تمرّر style جاي من المكتبة للتموضع الصحيح
return (
<div style={style} className="row">
الصف رقم {index}
</div>
);
}
export default function BigList({ items }) {
return (
<FixedSizeList
height={600} // ارتفاع منطقة العرض بالـ px
itemCount={items.length} // 10000 صف
itemSize={40} // ارتفاع كل صف ثابت بالـ px
width="100%"
overscanCount={5} // صفوف إضافية فوق وتحت النافذة
>
{Row}
</FixedSizeList>
);
}