أنبوب عرض المتصفح (Browser Rendering Pipeline)
🎯 السؤال الأساسي
لماذا تكون بعض صفحات الويب سلسة كالحرير، بينما تكون أخرى متقطعة كعرض PowerPoint؟ كيف يحول المتصفح كومة من أكواد HTML وCSS وJavaScript إلى صفحة الويب التي تراها أمامك؟ سيأخذك هذا الفصل في جولة داخل "ورشة عمل" المتصفح، لتفهم آلية عمله، ومن ثم تكتب صفحات ويب ذات أداء أفضل.
ماذا ستتعلم من هذه المقالة؟
| الفصل | المحتوى | ماذا ستستفيد بعد دراسته؟ |
|---|---|---|
| الفصل 1 | لماذا نفهم أنبوب العرض | فهم ضرورة تحسين الأداء |
| الفصل 2 | المراحل الخمس لأنبوب العرض | إتقان التدفق الأساسي لعرض المتصفح |
| الفصل 3 | بناء شجرة DOM وشجرة CSSOM | فهم كيفية تحليل HTML وCSS |
| الفصل 4 | بناء شجرة العرض | معرفة أي العناصر سيتم عرضها |
| الفصل 5 | التخطيط وإعادة التدفق | تجنب إثارة حسابات التخطيط المكلفة |
| الفصل 6 | الرسم وإعادة الرسم | تقليل عمليات الرسم غير الضرورية |
| الفصل 7 | التركيب وتسريع GPU | الاستفادة من GPU لتحسين أداء الرسوم المتحركة |
| الفصل 8 | حلقة الأحداث | فهم آلية تنفيذ JavaScript |
| الفصل 9 | تحسين الأداء العملي | إتقان تقنيات تحسين الأداء الشائعة |
يبدأ كل فصل من "فهم المبدأ"، ولا تحتاج إلى كتابة أكواد تحسين يدويًا. عندما تواجه مشكلة في الأداء، يمكنك العودة للمراجعة في أي وقت.
1. لماذا نفهم "أنبوب العرض"؟
1.1 من "يعمل" إلى "يعمل بسرعة": طريق التقدم في تطوير الواجهة الأمامية
عندما تبدأ تعلم الواجهة الأمامية، نهتم فقط بما إذا كان الكود "يعمل" — تعرض الصفحة، ويمكن النقر على الأزرار، وهذا يعتبر نجاحًا. لكن مع كبر المشاريع وزيادة المستخدمين، ستكتشف سريعًا حقيقة قاسية: نفس الوظيفة، شخص ما يكتب صفحة سلسة كالحرير، وآخر يكتب صفحة متقطعة تجعل المستخدم يريد تحطيم الفأرة.
هذا يشبه تعلم القيادة. المبتدئ يهتم فقط "هل يمكن للسيارة أن تتحرك"، لكن السائق الخبير يهتم "متى يجب تغيير الترس، ومتى يجب الفرامل، وكيف تقود بأقل استهلاك للوقود". المتصفح هو "السيارة" التي تقودها، افهم "طبيعة عمله"، لتتمكن من القيادة بسرعة وثبات.
🐢 عقلية المبتدئ (يهتم بالوظيفة فقط)
- المهم أن الصفحة تظهر فقط
- التقطع مشكلة المتصفح
- تحسين الأداء شيء يؤجل للنهاية
🚀 العقلية المتقدمة (تهتم بالتجربة)
- السلاسة هي جوهر تجربة المستخدم
- فهم آلية عمل المتصفح
- التفكير في الأداء أثناء كتابة الكود
فهم أنبوب العرض هو الخطوة الأساسية للانتقال من "يعمل" إلى "يعمل بسرعة".
1.2 قصة واقعية عن فخ أداء: لماذا أصبح "التحسين" أبطأ؟
قصة شياو تشانغ مع فخ الأداء
شياو تشانغ هو مهندس واجهة أمامية في شركة تجارة إلكترونية، مسؤول عن تحسين صفحة تفاصيل المنتج. كانت هذه الصفحة بطيئة جدًا عند عرض معلومات المنتج، وشكاوى المستخدمين لا تتوقف.
فكر شياو تشانغ: "الصفحة بطيئة لأن عناصر DOM كثيرة جدًا، سأخفيها أولاً بـ display:none، ثم أعرضها بعد التعديل، هكذا لن يكرر المتصفح العرض صحيح؟"
فكتب هذا الكود:
// "التحسين" الذي تظنه
const container = document.getElementById('list')
container.style.display = 'none' // أخفِ أولاً، لن يثير العرض صحيح؟
for (let i = 0; i < 1000; i++) {
const item = document.createElement('div')
item.style.width = Math.random() * 100 + 'px' // عرض عشوائي
container.appendChild(item)
}
container.style.display = 'block' // أظهر في النهاية، عرض مرة واحدةبعد الاختبار اكتشف أن الصفحة أصبحت أبطأ! احتار شياو تشانغ: من الواضح أنه "حسّن"، لماذا أصبح أبطأ؟
لاحقًا، نظر مدير الواجهة الأمامية في الكود، وأشار إلى المشكلة: رغم أن العناصر مخفية، إلا أن كل تعديل لـ style.width لا يزال يثير حساب الأنماط وعلامات التخطيط في المتصفح، مما يجعل المتصفح يقوم بعمل غير مجدٍ في الخلفية.
الطريقة الصحيحة هي استخدام DocumentFragment للعمل الدفعي في الذاكرة، ثم إدخاله في DOM دفعة واحدة في النهاية، مما يثير العرض مرة واحدة فقط.
💡 الدرس الأساسي
بدون فهم آلية عمل المتصفح، قد "تكون ذكيًا بشكل خاطئ" وتكتب كومة من "أكواد التحسين"، لتجعل الأداء أسوأ في النهاية. فهم أنبوب العرض يجعلك تعرف أي العمليات مكلفة وأيها رخيصة، وبالتالي تتجنب بذل الجهد في المكان الخطأ.
2. المفهوم الأساسي: ما هو "أنبوب العرض"؟
🤔 ما هو "العرض"؟
العرض (Rendering)، ببساطة هو عملية "رسم" المتصفح للكود ليصبح صفحة الويب التي تراها.
يمكنك تخيله كـ مطبعة كتب:
- HTML = محتوى المخطوطة (نصوص، صور، فصول)
- CSS = متطلبات التنسيق (حجم الخط، اللون، المسافات)
- JavaScript = التعديل الديناميكي (تعديلات الكاتب المؤقتة، تعديل التنسيق)
بعد أن يحصل المتصفح على هذه "المواد"، يمر بعدة "مراحل" قبل أن "يطبع" صفحة الويب التي تراها. هذه السلسلة من المراحل هي أنبوب العرض (Rendering Pipeline).
لمساعدتك على الفهم بشكل أفضل، نستخدم مخبزًا كتشبيه لعملية عرض المتصفح.
2.1 فهم أنبوب العرض من خلال تشبيه المخبز
تخيل أنك تدير مخبزًا، وعليك كل يوم صنع أنواع مختلفة من الخبز للعملاء. المراحل المتضمنة في هذه العملية تشبه بشكل مذهل عملية عرض المتصفح:
| المرحلة | 🥖 تشبيه المخبز | العمل الفعلي للمتصفح | مثال ملموس |
|---|---|---|---|
| 1. تحضير المكونات | ترتيب قائمة المكونات (دقيق، بيض، كريمة...) | بناء شجرة DOM: تحليل HTML إلى هيكل شجري | تكتب <div><p>Hello</p></div>، يحللها المتصفح إلى شجرة div→p→"Hello" |
| 2. تحضير الوصفة | ترتيب بطاقات الوصفات (نسب مكونات كل نوع خبز) | بناء شجرة CSSOM: تحليل CSS إلى شجرة قواعد | تكتب .title { color: red }، يسجل المتصفح "نص .title باللون الأحمر" |
| 3. وضع الخطة | حسب المكونات والوصفات، تحديد ما سيتم خبزه اليوم | بناء شجرة العرض: دمج DOM وCSSOM، والاحتفاظ فقط بالعناصر المرئية | وسم <script> لا يُعرض، لذا ليس في شجرة العرض |
| 4. وضع الخبز | وضع الخبز في واجهة العرض، وتحديد مكان كل رغيف | التخطيط (Layout): حساب أبعاد وموقع كل عنصر | حساب "هذا div عرضه 200px وارتفاعه 100px، في الموقع (50, 50) من الشاشة" |
| 5. التزيين | دهن الخبز بالبيض، رش السمسم، وضع الكريمة | الرسم (Paint): "رسم" اللون والحدود والظلال لكل عنصر | رسم "النص الأحمر" فعليًا على الشاشة |
| 6. التجميع النهائي | ترتيب كل أنواع الخبز معًا بشكل جميل | التركيب (Composite): دمج الطبقات المتعددة في الصورة النهائية | GPU يدمج طبقة الخلفية والنصوص والصور في صورة كاملة واحدة |
📊 ماذا يمكنك أن ترى من الجدول؟
دعنا نفسر هذا الجدول سطرًا بسطر، لفهم كل مرحلة من أنبوب العرض:
المرحلة 1-2 (مرحلة التحضير): المتصفح "يفهم" كودك أولاً. HTML وCSS يتم تحليلهما بشكل منفصل، لأن مسؤولياتهما مختلفة — HTML يحدد "ما هو المحتوى"، وCSS يحدد "كيف يبدو".
المرحلة 3 (مرحلة الدمج): لماذا "الدمج"؟ لأنه ليس كل عناصر HTML ستُعرض (مثل <head>، <script>)، يحتاج المتصفح لدمج "العناصر المرئية" مع "أنماطها" لتشكيل "مخطط البناء".
المرحلة 4-5 (مرحلة الرسم): التخطيط هو "حساب الموقع"، والرسم هو "وضع اللون". تغيير التخطيط (مثل تغيير العرض) يثير الرسم، لكن تغيير الرسم (مثل تغيير اللون) لا يثير التخطيط.
المرحلة 6 (مرحلة التركيب): "سحر" المتصفحات الحديثة. الطريقة التقليدية هي "الرسم دفعة واحدة" (CPU بطيء)، والطريقة الحديثة هي "الرسم الطبقي + تركيب GPU" (سريع)، وهذا هو سبب أن حركة transform أكثر سلاسة من حركة width.
2.2 المراحل الخمس لأنبوب العرض
3. المرحلة الأولى: بناء شجرة DOM وشجرة CSSOM
3.1 لماذا "الشجرة"؟
🤔 ما هو DOM؟
DOM (Document Object Model، نموذج كائن المستند)، هو هيكل شجري يحول المتصفح مستند HTML إليه، لتسهيل معالجة JavaScript لعناصر الصفحة.
يمكنك تخيله كـ شجرة العائلة:
- في الأعلى "الجد" (
<html>) - تحته "الأبناء" (
<body>،<head>) - وتحته "الأحفاد" (
<div>،<p>،<span>)
لماذا التحويل إلى شجرة؟ لأن الهيكل الشجري مناسب جدًا "للبحث" و"التعديل". مثلاً، إذا أردت العثور على "كل العناصر التي صنفها title"، يمكن للمتصفح البحث بسرعة في الشجرة، بدلاً من البحث في نصوص غير مرتبة ببطء.
بعد أن يحصل المتصفح على HTML، لا يعرضه فورًا، بل "يفهمه" أولاً. هذه العملية تنقسم إلى ثلاث خطوات:
الخطوة الأولى: التحليل المعجمي — تفكيك الكود إلى "كلمات"
<div class="container">
<p>Hello World</p>
</div>عندما يرى المتصفح هذا الكود، "يفككه" أولاً:
<div>→ "وسم بداية div"class="container"→ "خاصية class، قيمتها container"<p>→ "وسم بداية p"Hello World→ "محتوى نصي"</p>→ "وسم نهاية p"</div>→ "وسم نهاية div"
الخطوة الثانية: التحليل النحوي — تجميع "الكلمات" إلى "عقد"
حسب قواعد HTML، يجمع المتصفح هذه "الكلمات" إلى "عقد":
- عقدة عنصر:
<div>،<p> - عقدة خاصية:
class="container" - عقدة نصية:
"Hello World"
الخطوة الثالثة: بناء الشجرة — إنشاء "علاقات الأب والابن"
أخيرًا، حسب علاقات التداخل بين الوسوم، يبني المتصفح الهيكل الشجري:
Document (عقدة جذر المستند)
└── html
└── body
└── div.class = "container"
└── p
└── "Hello World"3.2 شجرة CSSOM: "دليل القواعد" للأنماط
🤔 ما هو CSSOM؟
CSSOM (CSS Object Model، نموذج كائن CSS)، هو هيكل شجري يحول المتصفح قواعد CSS إليه، لاستخدامه في حساب النمط النهائي لكل عنصر.
يمكنك تخيله كـ دليل تنسيق الملابس:
- القواعد العلوية (مثل خط body) تؤثر على السفلية (كل العناصر الابنة)
- إذا كان هناك تعارض (مثلاً عدة قواعد تحدد ألوانًا مختلفة لنفس العنصر)، يتم التحديد حسب "الأولوية"
- في النهاية يحسب ماذا "يرتدي" كل عنصر
عملية بناء CSSOM تشبه DOM، لكن بفارق أساسي: CSS "يُورث" و"يتتالي".
عرض عملية بناء CSSOM
CSS الأصلي:
body {
font-size: 16px;
color: #333;
}
.container {
width: 100%;
color: red; /* سيغطي color الخاص بـ body */
}
.container p {
font-weight: bold;
}شجرة CSSOM بعد البناء:
StyleSheet
├── body
│ ├── font-size: 16px
│ └── color: #333
└── .container
├── width: 100%
├── color: red (أولوية أعلى، تغطي color الخاص بـ body)
└── p
└── font-weight: bold3.3 سجل الفخاخ: لماذا CSS الخاص بي "لا يعمل"؟
الفخ الأول: تعارض أوزان محددات CSS
عرض الأخطاء الشائعة
/* كود CSS الذي كتبته */
#header { color: red; } /* محدد id، وزنه 100 */
.title { color: blue; } /* محدد class، وزنه 10 */
/* HTML */
<div id="header" class="title">ما لون هذا النص؟</div>تظن أنه أزرق، لكن النتيجة أحمر. لأن وزن محدد id (100) أعلى من محدد class (10).
الفخ الثاني: وسوم HTML غير مغلقة، المتصفح "يصلحها تلقائيًا"
عرض كيف يصلح المتصفح HTML الخاطئ
<!-- HTML الذي كتبته -->
<div>
<p>هذا نص
</div>
<!-- بعد إصلاح المتصفح -->
<div>
<p>هذا نص</p> <!-- المتصفح يغلق الوسم تلقائيًا نيابة عنك -->
</div>المتصفح "متسامح" جدًا، يصلح أخطائك تلقائيًا. لكن هذا التسامح له ثمن — يحتاج المتصفح لحسابات إضافية لتخمين نيتك، مما يؤثر على الأداء.
4. المرحلة الثانية: بناء شجرة العرض
4.1 لماذا نحتاج "شجرة العرض"؟
قد تسأل: "لدينا شجرة DOM وشجرة CSSOM، لماذا نبني شجرة عرض أخرى؟ ألا يمكن استخدام DOM مباشرة؟"
الجواب: شجرة DOM تحتوي على الكثير من المعلومات "غير المفيدة".
مثلاً هذا الكود:
<html>
<head>
<title>عنوان الصفحة</title>
<style>/* كود CSS */</style>
<script>/* كود JavaScript */</script>
</head>
<body>
<div class="container">
<p>محتوى مرئي</p>
</div>
<div style="display: none">
<p>محتوى مخفي (display:none)</p>
</div>
</body>
</html>شجرة DOM تحتوي كل العناصر:
<head>،<title>،<style>،<script>(هذه لا تُعرض)- div بـ
display: none(لا يُعرض أيضًا)
لكن شجرة العرض تحتوي فقط العناصر "التي سترسم على الشاشة":
- إزالة
<head>وعناصره الفرعية - إزالة div بـ
display: none
4.2 قواعد بناء شجرة العرض
عند بناء شجرة العرض، يتبع المتصفح مجموعة من القواعد:
| السيناريو | طريقة المعالجة | مثال | التأثير على الأداء |
|---|---|---|---|
display: none | استبعاد كامل من شجرة العرض | العنصر وعناصره الفرعية كلها غير مرئية | ✅ يقلل عبء العرض |
visibility: hidden | موجود في شجرة العرض، لكن لا يُرسم | يشغل مساحة، لكنه شفاف تمامًا | ⚠️ لا يزال يحتاج حساب التخطيط |
opacity: 0 | موجود في شجرة العرض، لكنه شفاف | يمكن التفاعل معه (قابل للنقر)، لكنه غير مرئي | ⚠️ لا يزال يحتاج حساب التخطيط |
| خارج نطاق الرؤية | موجود في شجرة العرض، لا يُرسم مؤقتًا | يُرسم عند التمرير إلى نطاق الرؤية | ⚠️ لكنه لا يزال في شجرة العرض |
📊 ماذا يمكنك أن ترى من الجدول؟
الاكتشاف الأساسي: display: none هو الطريقة الوحيدة "الموفرة للأداء حقًا" للإخفاء، لأن العنصر خارج شجرة العرض تمامًا، ولا يقوم المتصفح بأي تخطيط أو رسم له.
أما visibility: hidden وopacity: 0 فرغم أنهما "غير مرئيين"، إلا أنهما لا يزالان في شجرة العرض، ويحتاج المتصفح لحساب تخطيطهما (يشغلان مساحة). إذا كنت بحاجة إلى "إخفاء دون التأثير على التخطيط" (مثل حركة التلاشي)، استخدم opacity؛ وإذا كنت بحاجة إلى "إخفاء كامل دون شغل مساحة"، استخدم display: none.
4.3 سجل الفخاخ: لماذا تظل الصفحة بطيئة رغم ضبط display:none؟
❌ سوء فهم شائع: الاعتقاد أن عناصر display:none "غير موجودة"
يعتقد الكثيرون أنه بعد ضبط display: none، "يختفي" العنصر، ولا تؤثر أي عملية عليه على الأداء. هذا خاطئ!
رغم أن عناصر display: none ليست في شجرة العرض، إلا أنه عند تعديل خصائصها عبر JavaScript، لا يزال المتصفح بحاجة إلى:
- إعادة حساب الأنماط (مطابقة قواعد CSS)
- تتبع التغييرات (استعدادًا للعرض المستقبلي)
انظر إلى مثال "التحسين" التالي:
عرض كود "التحسين غير الفعال"
// ❌ "التحسين" الذي تظنه: أخفِ أولاً، عدل ثم أظهر
const container = document.getElementById('list')
container.style.display = 'none'
// عمليات DOM مجنونة
for (let i = 0; i < 1000; i++) {
const item = document.createElement('div')
item.style.width = Math.random() * 100 + 'px' // تغيير العرض!
item.textContent = `Item ${i}`
container.appendChild(item)
}
container.style.display = 'block'
// المشكلة: كل تعديل لـ style.width يجبر المتصفح على إعادة حساب الأنماط،
// حتى لو كان العنصر display:none!✅ الطريقة الصحيحة للتحسين:
// استخدام DocumentFragment للعمل الدفعي
const container = document.getElementById('list')
const fragment = document.createDocumentFragment() // حاوية افتراضية
// كل العمليات تتم على fragment في الذاكرة
for (let i = 0; i < 1000; i++) {
const item = document.createElement('div')
item.style.width = Math.random() * 100 + 'px'
item.textContent = `Item ${i}`
fragment.appendChild(item) // لا يؤثر على DOM الحقيقي
}
// إدخال دفعة واحدة في DOM الحقيقي، يثير العرض مرة واحدة فقط
container.appendChild(fragment)5. المرحلة الثالثة: التخطيط وإعادة التدفق
5.1 ما هو "التخطيط"؟
🤔 ما هو التخطيط (Layout)؟
التخطيط، ويسمى أيضًا إعادة التدفق (Reflow)، هو عملية حساب المتصفح "أين يقع كل عنصر وكم مساحة يشغلها" في شجرة العرض.
يمكنك تخيله كـ مصمم ديكور يقيس الغرفة:
- أولاً يقيس طول وعرض كل غرفة
- يقرر أين يوضع الأثاث
- يحسب إحداثيات كل قطعة أثاث
لماذا التخطيط "مكلف"؟ لأن تغيير عنصر واحد قد يؤثر على عناصر أخرى. مثلاً، إذا جعلت div أوسع، قد يندفع div المجاور للأسفل، مما يؤدي إلى إعادة حساب الصفحة بأكملها.
5.2 "حقول الألغام" التي تثير إعادة التدفق
فيما يلي العمليات الشائعة التي تثير إعادة التدفق، يُنصح بحفظها:
| الفئة | الخاصية/العملية | التأثير على الأداء | البديل |
|---|---|---|---|
| الأبعاد | width, height, min/max-width/height | 💀💀💀 | استخدم transform: scale() بدلاً منها |
| الموقع | top, right, bottom, left | 💀💀💀 | استخدم transform: translate() بدلاً منها |
| الهوامش | margin, padding | 💀💀 | استخدم transform أو gap بدلاً منها |
| الحدود | border-width | 💀💀 | تجنب التعديل المتكرر قدر الإمكان |
| المحتوى | تغيير محتوى النص، تحميل الصور | 💀💀 | احجز مساحة مسبقًا لتجنب اهتزاز التخطيط |
| الخط | font-size, line-height | 💀💀💀 | تجنب التعديل المتكرر قدر الإمكان |
| العرض | تغيير قيمة display | 💀💀💀 | استخدم visibility أو opacity بدلاً منها (إذا لم تكن بحاجة للإخفاء الكامل) |
| الاستعلام | offsetWidth, offsetHeight وغيرها | 💀💀💀💀💀 | القراءة الدفعية، تجنب اهتزاز التخطيط |
📊 ماذا يمكنك أن ترى من الجدول؟
الاكتشافات الأساسية:
- خصائص الهندسة (العرض، الارتفاع، الموقع) هي الأكثر تكلفة: تثير حساب التخطيط الكامل
- خصائص الاستعلام أخطر من التعديل: قراءة
offsetWidthتجبر التخطيط المتزامن (انظر القسم 5.4) - transform وopacity هما الأفضل أداءً: لا يثيران إعادة التدفق، بل يثيران التركيب فقط
5.3 سجل الفخاخ: لماذا رسومي المتحركة متقطعة كعرض PowerPoint؟
الفخ: استخدام width للرسوم المتحركة
عرض كود الرسوم المتحركة سيئة الأداء
/* ❌ رسوم متحركة سيئة: تثير إعادة التدفق */
.box {
width: 100px;
transition: width 0.3s;
}
.box:hover {
width: 200px; /* تغيير العرض يثير إعادة التدفق! */
}كل إطار من الرسوم المتحركة يثير إعادة التدفق، ويحتاج المتصفح إلى:
- إعادة حساب العرض
- إعادة حساب الموقع (قد يؤثر على عناصر أخرى)
- إعادة الرسم
✅ رسوم متحركة جيدة: استخدام transform
/* ✅ رسوم متحركة جيدة: تثير التركيب فقط */
.box {
width: 100px;
transform: scaleX(1);
transition: transform 0.3s;
}
.box:hover {
transform: scaleX(2); /* التدوير لا يثير إعادة التدفق! */
}transform يعالج مباشرة بواسطة GPU، لا يثير إعادة التدفق ولا إعادة الرسم، الرسوم المتحركة سلسة كالحرير.
5.4 قاتل الأداء: التخطيط المتزامن القسري
💀 أخطر مشكلة أداء: اهتزاز التخطيط
التخطيط المتزامن القسري (Forced Synchronous Layout)، ويسمى أيضًا اهتزاز التخطيط (Layout Thrashing)، هو أكثر مشاكل الأداء شيوعًا وخطورة.
سببه: عندما تقرأ JavaScript خصائص التخطيط (مثل offsetWidth)، يجب على المتصفح تنفيذ حساب التخطيط فورًا ليرجع قيمة دقيقة.
إذا تبادلت "القراءة والكتابة"، فسيؤدي ذلك إلى تكرار المتصفح "تخطيط → قراءة → تخطيط → قراءة"، مما يشكل حلقة مفرغة.
عرض كود اهتزاز التخطيط
// ❌ سيء جدًا: تبادل القراءة والكتابة، يؤدي إلى اهتزاز التخطيط
const elements = document.querySelectorAll('.item')
for (let i = 0; i < elements.length; i++) {
const height = elements[i].offsetHeight // قراءة → تخطيط قسري
elements[i].style.width = (height * 2) + 'px' // كتابة → تعليم الحاجة لإعادة التدفق
// قراءة الدورة التالية ستجبر التخطيط مرة أخرى... حلقة مفرغة!
}
// إذا كان هناك 100 عنصر، سيتم إثارة 100 عملية حساب تخطيط!✅ الطريقة الصحيحة للتحسين: فصل القراءة عن الكتابة
const elements = document.querySelectorAll('.item')
// الخطوة الأولى: القراءة الدفعية (اقرأ الكل أولاً)
const heights = []
for (let i = 0; i < elements.length; i++) {
heights.push(elements[i].offsetHeight) // يثير التخطيط مرة واحدة فقط
}
// الخطوة الثانية: الكتابة الدفعية (ثم اكتب الكل)
requestAnimationFrame(() => {
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = (heights[i] * 2) + 'px' // يثير إعادة التدفق مرة واحدة فقط
}
})6. المرحلة الرابعة: الرسم وإعادة الرسم
6.1 ما هو "الرسم"؟
🤔 ما هو الرسم (Paint)؟
الرسم، هو عملية "رسم" المتصفح للعناصر التي تم "حساب تخطيطها" فعليًا على الشاشة.
يمكنك تخيله كـ دهان الغرفة:
- مرحلة التخطيط = قياس الأبعاد، رسم الخطوط
- مرحلة الرسم = الدهان الفعلي، لصق ورق الجدران
الرسم ليس مكلفًا كالتخطيط، لكنه ليس رخيصًا أيضًا. الرسم المتكرر لا يزال يؤثر على الأداء، خاصة للعناصر المعقدة (الظلال، التدرجات، إلخ).
6.2 إشارات إثارة إعادة الرسم
على عكس إعادة التدفق، إعادة الرسم تتعلق فقط بتغيير "المظهر"، وليس "الهندسة":
| الفئة | الخاصية | التأثير على الأداء | ملاحظات |
|---|---|---|---|
| اللون | color, background-color | 💀 | أكثر مثيرات إعادة الرسم شيوعًا |
| الخلفية | background-image, background-position | 💀💀 | الصور أبطأ من الألوان الصلبة |
| الحدود | border-color, border-style | 💀 | تغيير لون/نمط الحدود |
| النص | text-decoration, text-shadow | 💀💀 | الظلال أبطأ من النص العادي |
| ظل الصندوق | box-shadow | 💀💀💀 | الظلال المعقدة بطيئة جدًا |
| تدوير الزوايا | border-radius | 💀 | تغيير حجم التدوير |
| الشفافية | opacity | ✅ | خاص: لا يثير إعادة الرسم، يثير التركيب فقط |
📊 ماذا يمكنك أن ترى من الجدول؟
الاكتشاف الأساسي: opacity خاص! إنه مثل transform، لا يثير إعادة الرسم، بل يثير مرحلة التركيب مباشرة. هذا هو سبب أن حركة التلاشي بـ opacity هي الأفضل أداءً.
بالإضافة إلى ذلك، الظلال والتدرجات أغلى من إعادة الرسم، لأنها تحتاج حسابات بكسل معقدة. إذا كانت صفحتك تحتوي على الكثير من box-shadow، فكر في استخدام العناصر الوهمية أو الصور بدلاً منها.
6.3 سجل الفخاخ: لماذا تأثير hover الخاص بي بطيء؟
الفخ: استخدام box-shadow لحركة hover
عرض تأثير hover سيئ الأداء
/* ❌ تأثير hover سيئ: حركة box-shadow بطيئة جدًا */
.card {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s;
}
.card:hover {
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* الظل بطيء جدًا! */
}box-shadow يحتاج حساب كل بكسل، مما يسبب تقطعًا أثناء الحركة.
✅ الطريقة الجيدة: استخدام transform أو العناصر الوهمية
/* ✅ تأثير hover جيد: استخدام transform */
.card {
transform: translateY(0);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-4px); /* غير الظل فقط عند hover، لا تحركه */
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}transform: translate3d(0,0,0)任何3D变换都会创建新层opacity配合transition使用时position: fixed固定定位元素需要独立层will-change: transform显式提示浏览器创建层7. المرحلة الخامسة: التركيب وتسريع GPU
7.1 ما هو "التركيب"؟
🤔 ما هو التركيب (Composite)؟
التركيب، هو "سحر" المتصفحات الحديثة، حيث يقسم أجزاء الصفحة المختلفة إلى طبقات (Layer) متعددة، ثم يستخدم GPU (معالج الرسوميات) لتركيب الصورة النهائية بشكل متوازٍ.
يمكنك تخيله كـ طبقات Photoshop:
- الطريقة التقليدية = كل شيء مرسوم على طبقة واحدة (CPU تسلسلي، بطيء)
- طريقة التركيب = الرسم الطبقي، ثم الدمج النهائي (GPU متوازٍ، سريع)
لماذا التركيب سريع؟ لأن GPU ماهر في معالجة مهام "تركيب الصور" المتوازية، أسرع بعشرات المرات من CPU.
7.2 أي العناصر تُرفع إلى "طبقة التركيب"؟
يرفع المتصفح تلقائيًا بعض العناصر إلى طبقات تركيب مستقلة. فيما يلي شروط الإثارة الشائعة:
| شرط الإثارة | خاصية/قيمة CSS | التأثير على الأداء | ملاحظات |
|---|---|---|---|
| تحويل ثلاثي الأبعاد | transform: translate3d(), rotate3d() | ✅✅✅ | أفضل أداء للرسوم المتحركة |
| خدعة تسريع العتاد | transform: translateZ(0) | ✅✅ | تعرف شعبيًا بـ "تسريع GPU القسري" |
| حركة الشفافية | تغيير opacity (مع الحركة) | ✅✅✅ | لا يثير إعادة الرسم |
| تحديد المواقع الثابت | position: fixed | ✅ | يتجنب إعادة التخطيط عند التمرير |
| Will-Change | will-change: transform, opacity | ✅✅ | ينشئ الطبقة مسبقًا، انتبه للذاكرة |
| Canvas/WebGL | <canvas>, محتوى WebGL | ✅✅ | في طبقة مستقلة طبيعيًا |
| Video | <video> | ✅✅ | طبقة مستقلة، تمنع التأثير المتبادل |
📊 ماذا يمكنك أن ترى من الجدول؟
الاكتشاف الأساسي: transform وopacity هما أفضل خصائص الرسوم المتحركة أداءً، لأنهما لا يثيران إعادة التدفق وإعادة الرسم، بل يثيران التركيب مباشرة. هذا هو سبب أن أدلة تحسين الأداء تقول دائمًا "استخدم transform وopacity للرسوم المتحركة".
لكن انتبه: كل طبقة تركيب تستهلك ذاكرة GPU، وإساءة استخدام translateZ(0) ستؤدي إلى انفجار الذاكرة (انظر القسم 7.4).
7.3 سجل الفخاخ: طبقات التركيب الكثيرة تجعلها أبطأ؟
💀 فخ التحسين المفرط
سمع البعض أن "تسريع GPU سريع"، فأضافوا transform: translateZ(0) لكل العناصر، لتصبح الصفحة أبطأ في النهاية.
سبب المشكلة: كل طبقة تركيب تحتاج تخزين "نسيج" (صورة نقطية) في GPU، مما يستهلك ذاكرة. إذا كانت الصفحة تحتوي 100 طبقة تركيب، قد تنفجر ذاكرة GPU، مما يؤدي إلى تعطل الأجهزة الضعيفة أو التراجع إلى عرض CPU.
عرض كود "التحسين المفرط"
/* ❌ ممارسة خاطئة: تفعيل تسريع GPU لكل العناصر */
.card { transform: translateZ(0); }
.button { transform: translateZ(0); }
.icon { transform: translateZ(0); }
/* ... 100 عنصر كلها مضاف لها ... */
/* النتيجة: انفجار ذاكرة GPU، تجمد الصفحة */✅ الممارسة الصحيحة: الاستخدام حسب الحاجة
/* الاستراتيجية 1: تفعيل فقط للعناصر التي تحتاج حركة فعلاً */
.card {
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px); /* إنشاء طبقة تركيب تلقائيًا */
}
/* الاستراتيجية 2: استخدام will-change لإشعار المتصفح */
.card {
will-change: transform; /* إنشاء الطبقة مسبقًا */
}
/* الاستراتيجية 3: إزالة بعد انتهاء الحركة */
.card:not(:hover) {
will-change: auto; /* تحرير ذاكرة GPU */
}8. حلقة الأحداث: "تقنية التعدد" في JavaScript
🤔 ما هي حلقة الأحداث؟
حلقة الأحداث (Event Loop)، هي آلية JavaScript لتحقيق "اللا تزامن". لأن JavaScript أحادي الخيط (يستطيع فعل شيء واحد فقط في كل مرة)، لكنه يحتاج لمعالجة مهام متعددة مثل نقرات المستخدم وطلبات الشبكة والمؤقتات، لذا يحتاج "نظام جدولة" لإدارة هذه المهام.
يمكنك تخيلها كـ مركز فرز الطرود:
- Call Stack (مكدس الاستدعاء) = الطرد الذي تتم معالجته حاليًا
- Web APIs = مستودعات التعاون الخارجية (المؤقتات، طلبات الشبكة، إلخ)
- Callback Queue (طابور الردود) = رف الطرود المنتظرة
- Event Loop (حلقة الأحداث) = روبوت الفرز (يتحقق باستمرار "هل يمكن معالجة المهمة التالية")
8.1 المهام الكبرى والمهام الصغرى
في البداية، كان لدى JavaScript طابور مهام واحد فقط. لكن مع تعقيد البرمجة اللا تزامنية، قدمت المتصفحات نوعين من المهام:
| النوع | المصادر الشائعة | الأولوية | توقيت التنفيذ |
|---|---|---|---|
| المهمة الكبرى | setTimeout/setInterval، عمليات I/O، عرض UI | منخفضة | تنفيذ واحدة في كل دورة من حلقة الأحداث |
| المهمة الصغرى | Promise.then، MutationObserver | عالية | بعد انتهاء المهمة الكبرى الحالية، تفرغ فورًا كل المهام الصغرى |
"قاعدة" ترتيب التنفيذ:
1. تنفيذ المهمة الكبرى الحالية (مثل <script> بالكامل)
2. تنفيذ كل المهام الصغرى الناتجة أثناء التنفيذ (Promise.then وغيرها)
↳ المهام الصغرى يمكن أن تنتج مهامًا صغرى جديدة، تفرغ كلها قبل المتابعة
3. إذا لزم الأمر، عرض UI (إعادة تدفق/إعادة رسم)
4. بدء دورة حلقة الأحداث التالية، تنفيذ المهمة الكبرى التالية8.2 سجل الفخاخ: Promise أسرع من setTimeout؟
❌ سوء فهم شائع: setTimeout(fn, 0) سينفذ "فورًا"
يعتقد الكثيرون أن setTimeout(fn, 0) تعني "التنفيذ فورًا بعد 0 ميلي ثانية"، وهذا فهم خاطئ.
في الواقع، معنى setTimeout(fn, 0) هو: "انتظر 0 ميلي ثانية على الأقل، ثم أضف الرد إلى طابور المهام الكبرى". لكنه يحتاج انتظار تفريغ مكدس الاستدعاء الحالي، وتفريغ طابور المهام الصغرى، وربما عرض UI، قبل أن يتمكن من التنفيذ.
عرض ترتيب التنفيذ
console.log('1. Start')
setTimeout(() => {
console.log('2. setTimeout callback')
}, 0)
Promise.resolve().then(() => {
console.log('3. Promise.then')
})
console.log('4. End')
// ترتيب الإخراج الذي تظنه:
// 1. Start
// 4. End
// 2. setTimeout callback ← setTimeout(0) أليس فوريًا؟
// 3. Promise.then
// ترتيب الإخراج الفعلي:
// 1. Start
// 4. End
// 3. Promise.then ← Promise.then ينفذ قبل setTimeout!
// 2. setTimeout callbackرسم توضيحي لتدفق التنفيذ:
مكدس الاستدعاء (Call Stack) طابور المهام الكبرى طابور المهام الصغرى
[setTimeout callback] [Promise.then callback]
1. console.log('1. Start')
→ إخراج: 1. Start
2. setTimeout(fn, 0)
→ إضافة الرد إلى طابور المهام الكبرى ← [setTimeout callback]
3. Promise.resolve().then()
→ إضافة الرد إلى طابور المهام الصغرى ← [Promise.then callback]
4. console.log('4. End')
→ إخراج: 4. End
5. تفريغ مكدس الاستدعاء، فحص طابور المهام الصغرى
→ العثور على رد Promise.then
→ تنفيذ: console.log('3. Promise.then')
→ إخراج: 3. Promise.then
6. تفريغ طابور المهام الصغرى
→ قد يحتاج عرض UI (إذا كان هناك تغيير)
7. فحص طابور المهام الكبرى
→ العثور على رد setTimeout
→ تنفيذ: console.log('2. setTimeout callback')
→ إخراج: 2. setTimeout callback💡 الدرس الأساسي
المهام الصغرى "أكثر استعجالاً" من المهام الكبرى. إذا كنت تريد أن تنفذ عملية ما "بعد انتهاء كتلة الكود الحالية، لكن قبل تحديث UI"، استخدم Promise.then أو queueMicrotask.
setTimeout(0) لا يضمن التنفيذ الفوري، سيتم تأخيره على الأقل حتى تفريغ مكدس الاستدعاء الحالي وتفريغ طابور المهام الصغرى.
Event Loop: How JavaScript Executes Code
Code queue
Worker (single thread)
Task queue
Output log
Execution order: not started
Written order: 1, 2, 3, 4, 5
Code is written top to bottom, but it does not always run top to bottom because async work is delayed until the current code finishes.
console.log('1')
setTimeout(() => console.log('2'), 0) // 宏任务
Promise.resolve().then(() => console.log('3')) // 微任务
console.log('4')
// 输出顺序:1 → 4 → 3 → 29. تحسين الأداء العملي: اجعل صفحتك "تطير"
بعد فهم آلية عمل أنبوب العرض، دعنا نرى كيفية التحسين. فيما يلي خمسة من أكثر تقنيات التحسين عملية.
9.1 القاعدة الذهبية: تجنب التخطيط المتزامن القسري
المشكلة: تبادل قراءة وكتابة خصائص التخطيط، مما يؤدي إلى اهتزاز التخطيط.
عرض المقارنة قبل وبعد التحسين
// ❌ سيء جدًا: تبادل القراءة والكتابة، يؤدي إلى اهتزاز التخطيط
for (let i = 0; i < elements.length; i++) {
const height = elements[i].offsetHeight // قراءة → تخطيط قسري
elements[i].style.height = (height * 2) + 'px' // كتابة → تعليم الحاجة لإعادة التدفق
// قراءة الدورة التالية ستجبر التخطيط مرة أخرى... حلقة مفرغة!
}
// ✅ ممتاز: اقرأ الكل أولاً، ثم اكتب الكل
// الخطوة الأولى: القراءة الدفعية
const heights = []
for (let i = 0; i < elements.length; i++) {
heights.push(elements[i].offsetHeight)
}
// الخطوة الثانية: الكتابة الدفعية
requestAnimationFrame(() => {
for (let i = 0; i < elements.length; i++) {
elements[i].style.height = (heights[i] * 2) + 'px'
}
})9.2 استخدام transform وopacity للرسوم المتحركة
المشكلة: استخدام width وheight وleft وtop للرسوم المتحركة يثير إعادة التدفق.
عرض المقارنة قبل وبعد التحسين
/* ❌ رسوم متحركة سيئة: تثير إعادة التدفق */
.box {
transition: width 0.3s, left 0.3s;
}
.box.moving {
width: 200px;
left: 100px;
}
/* ✅ رسوم متحركة جيدة: تثير التركيب فقط */
.box {
transition: transform 0.3s;
}
.box.moving {
transform: translateX(100px) scaleX(2);
}9.3 التمرير الافتراضي: حل مشكلة القوائم الكبيرة
المشكلة: عندما يصل عدد عناصر القائمة إلى الآلاف، يؤدي كثرة عقد DOM إلى مشاكل في الأداء.
الفكرة الأساسية: عرض فقط عناصر القائمة المرئية داخل نطاق الرؤية (مع إضافة قليل من التخزين المؤقت)، مما يجعل عدد عقد DOM ثابتًا بغض النظر عن إجمالي البيانات.
transform 和 opacity 做动画will-change 提前告知浏览器عرض تنفيذ التمرير الافتراضي
<template>
<div class="virtual-list" @scroll="handleScroll">
<!-- عنصر وهمي لتمديد شريط التمرير -->
<div class="phantom" :style="{ height: totalHeight + 'px' }"></div>
<!-- عناصر القائمة المعروضة فعليًا -->
<div class="content" :style="{ transform: `translateY(${offsetY}px)` }">
<div
v-for="item in visibleItems"
:key="item.id"
class="item"
:style="{ height: itemHeight + 'px' }"
>
{{ item.name }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const props = defineProps({
items: Array,
itemHeight: { type: Number, default: 50 }
})
const scrollTop = ref(0)
const buffer = 5 // عدد العناصر المؤقتة
// كم عنصرًا يمكن عرضه في المنطقة المرئية
const visibleCount = computed(() => 10)
// الفهرس الابتدائي
const startIndex = computed(() =>
Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - buffer)
)
// الفهرس النهائي
const endIndex = computed(() =>
Math.min(props.items.length, startIndex.value + visibleCount.value + buffer * 2)
)
// البيانات المرئية الحالية
const visibleItems = computed(() =>
props.items.slice(startIndex.value, endIndex.value)
)
// الارتفاع الإجمالي
const totalHeight = computed(() => props.items.length * props.itemHeight)
// الإزاحة
const offsetY = computed(() => startIndex.value * props.itemHeight)
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop
}
</script>9.4 منع الاهتزاز والتقييد: تقليل تكرار إثارة الأحداث
المشكلة: الأحداث المتكررة (مثل scroll وresize) تؤدي إلى مشاكل في الأداء.
عرض تنفيذ منع الاهتزاز والتقييد
// منع الاهتزاز (Debounce): تأخير التنفيذ، إذا تم الإثارة مرة أخرى خلال وقت التأخير، يعاد الضبط
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
// التقييد (Throttle): تنفيذ بفاصل زمني ثابت
function throttle(fn, interval) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// مثال على الاستخدام
window.addEventListener('scroll', debounce(handleScroll, 200))
window.addEventListener('resize', throttle(handleResize, 100))9.5 التحميل الكسول: تأخير تحميل الموارد غير الحرجة
المشكلة: تحميل الكثير من الموارد في الشاشة الأولى يؤدي إلى بطء فتح الصفحة.
عرض تنفيذ التحميل الكسول
// تحميل كسول للصور
const lazyImages = document.querySelectorAll('img[data-src]')
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src // تحميل الصورة الحقيقية
img.removeAttribute('data-src')
observer.unobserve(img) // توقف عن المراقبة
}
})
})
lazyImages.forEach(img => imageObserver.observe(img))10. مشاكل الأداء التي يجب أن تتعرف عليها الآن
بعد فهم أنبوب عرض المتصفح، يجب أن تكون قادرًا على التعرف على مشاكل الأداء الشائعة التالية:
| الكود المشكل | موضع المشكلة | كيف تصفها للذكاء الاصطناعي |
|---|---|---|
element.style.width = ... | تعديل العرض بشكل متكرر في حلقة | "هذا سيثير إعادة تدفق متعددة، يُرجى استخدام transform أو المعالجة الدفعية" |
height = element.offsetHeight | قراءة خصائص التخطيط مباشرة بعد الكتابة | "هذا تخطيط متزامن قسري، يُرجى فصل عمليات القراءة والكتابة" |
element.className = ... | تعديل متكرر لـ class يثير إعادة حساب الأنماط | "استخدم classList.add/remove بدلاً منه، لتقليل حساب الأنماط" |
حركة بـ width/left | يثير إعادة التدفق وإعادة الرسم، أداء سيئ | "استخدم transform وopacity للرسوم المتحركة" |
إضافة translateZ(0) لكل العناصر | إساءة استخدام تسريع GPU تؤدي إلى انفجار الذاكرة | "فعل تسريع GPU فقط للعناصر التي تحتاج حركة" |
| عرض 10000 عنصر قائمة كلها | كثرة عقد DOM تؤدي إلى تقطع | "نفذ التمرير الافتراضي، اعرض فقط المنطقة المرئية" |
| التلاعب بـ DOM مباشرة في حدث scroll | تكرار الإثارة العالي يؤدي إلى تقطع | "استخدم requestAnimationFrame أو التقييد للتحسين" |
box-shadow كحركة hover | حساب الظل المعقد بطيء جدًا | "استخدم transform أو العناصر الوهمية، تجنب تحريك الظل" |
إذا قرأت "سجل الفخاخ" في كل فصل بجدية، فأنت الآن تتقن هذه المفاهيم الأساسية:
- مراحل أنبوب العرض الخمس: DOM/CSSOM → شجرة العرض → التخطيط → الرسم → التركيب
- إعادة التدفق مقابل إعادة الرسم: إعادة التدفق هي الأكثر تكلفة (تغيير هندسي)، إعادة الرسم أقل (تغيير المظهر)
- التخطيط المتزامن القسري: تبادل القراءة والكتابة يؤدي إلى اهتزاز التخطيط، يجب الفصل بينهما
- تسريع GPU: transform وopacity يعالجان بواسطة GPU، الأفضل أداءً
- حلقة الأحداث: JavaScript أحادي الخيط، يحقق اللا تزامن من خلال طوابير المهام
هذه المفاهيم ستساعدك في تحديد اختناقات الأداء بسرعة.
💡 عند مواجهة مشاكل الأداء، قل للذكاء الاصطناعي هكذا
- "الرسوم المتحركة متقطعة، تحقق مما إذا كانت تثير إعادة تدفق أو إعادة رسم"
- "أداء التمرير سيئ، قد تحتاج تقييدًا أو requestAnimationFrame"
- "البيانات كبيرة الحجم تسبب تقطعًا في القائمة، تحتاج تمريرًا افتراضيًا"
- "تعديل الأنماط المتكرر يسبب مشكلة في الأداء، يُرجى التحسين باستخدام transform"
11. الخلاصة: جوهر تحسين أنبوب العرض
من خلال دراسة هذه المقالة، يمكننا استخلاص الاستنتاجات الأساسية التالية:
من الناحية العملية: ليس المهم أن يكون التحسين أكثر، بل أن يكون التحسين "في المكان الصحيح". فهم أنبوب عرض المتصفح يجعلك تعرف أين تبذل الجهد وأين تتركه.
من منظور التكلفة:
- معظم هدر الأداء يأتي من التبادل المتكرر لقراءة وكتابة خصائص التخطيط، ويحتاج إلى الفصل بين القراءة والكتابة والمعالجة الدفعية
- تأثيرات الرسوم المتحركة المعقدة إذا أثارت إعادة التدفق وإعادة الرسم، فغالبًا ما يكون السبب استخدام "خصائص خاطئة"، وتحتاج إلى
transformوopacityلحلها - عند مواجهة عرض قوائم بكميات كبيرة من البيانات، الاعتماد على Virtual DOM وحده لم يعد كافيًا، ويجب دمجه مع تقنيات مثل التمرير الافتراضي
الهدف هو: في ظل ظروف المتصفح والعتاد المحددة، جعل كل خطوة من خطوات العرض تحقق مكسبًا واضحًا في الأداء.
12. جدول المصطلحات
| المصطلح الإنجليزي | المقابل العربي | الشرح |
|---|---|---|
| DOM | نموذج كائن المستند | هيكل شجري يشكله المتصفح بعد تحليل مستند HTML، يمكن لـ JavaScript التلاعب بعناصر الصفحة عبر DOM API |
| CSSOM | نموذج كائن CSS | هيكل شجري يشكله المتصفح بعد تحليل CSS، يدمج مع DOM لحساب الأنماط النهائية |
| Render Tree | شجرة العرض | تدمج من شجرة DOM وشجرة CSSOM، تحتوي فقط العقد المرئية، تستخدم لحساب التخطيط والرسم اللاحقين |
| Layout | التخطيط | عملية حساب المعلومات الهندسية (الموقع، الحجم) لكل عقدة في شجرة العرض، تسمى أيضًا Reflow |
| Reflow | إعادة التدفق/الإعادة | عندما تتغير الخصائص الهندسية لعنصر (الأبعاد، الموقع)، يحتاج المتصفح لإعادة حساب التخطيط |
| Paint | الرسم/إعادة الرسم | عملية رسم أنماط العناصر (اللون، الخلفية، الحدود، إلخ) بعد حساب التخطيط على الشاشة |
| Repaint | إعادة الرسم | عندما تتغير خصائص المظهر لعنصر (كاللون، الخلفية) دون التأثير على الخصائص الهندسية، يثار تحديث الرسم |
| Composite | التركيب | عملية دمج طبقات الرسم المتعددة (Layer) في الصورة النهائية على الشاشة، تنفذ عادة على GPU |
| Layer | طبقة/طبقة تركيب | سطح رسم مستقل ينشئه المتصفح لتحسين العرض، يمكن تحويله وتركيبه بشكل منفصل |
| Event Loop | حلقة الأحداث | آلية تنفيذ JavaScript اللا تزامنية، مسؤولة عن جدولة تنفيذ المهام الكبرى والمهام الصغرى |
| Call Stack | مكدس الاستدعاء | هيكل بيانات يسجل دوال JavaScript التي يتم تنفيذها حاليًا |
| Macro Task | مهمة كبرى | نوع مهام بأولوية أقل في حلقة الأحداث، مثل setTimeout وsetInterval وعمليات I/O |
| Micro Task | مهمة صغرى | نوع مهام بأولوية أعلى في حلقة الأحداث، مثل Promise.then وMutationObserver |
| Forced Synchronous Layout | تخطيط متزامن قسري | تبادل قراءة وكتابة خصائص التخطيط في JavaScript، مما يجبر المتصفح على تنفيذ حساب التخطيط فورًا، مشكلة أداء |
| Layout Thrashing | اهتزاز التخطيط | ظاهرة انخفاض حاد في الأداء بسبب التخطيط المتزامن القسري المتكرر |
| Virtual Scrolling | تمرير افتراضي | تقنية لعرض فقط عناصر القائمة المرئية داخل نطاق الرؤية، لتحسين أداء القوائم كبيرة البيانات |
| RAF | طلب إطار الحركة | API يوفره المتصفح لتنفيذ كود JavaScript المتعلق بالرسوم المتحركة قبل إعادة الرسم التالية |