تجنُّب التخطيطات الكبيرة والمعقدة واستبدال التنسيقات

تاريخ النشر: 20 آذار (مارس) 2015، تاريخ آخر تعديل: 7 أيار (مايو) 2025

في قسم "التنسيق"، يحدّد المتصفّح المعلومات الهندسية للعناصر: حجمها وموقعها في الصفحة. سيتضمّن كل عنصر معلومات صريحة أو ضمنية عن الحجم استنادًا إلى لغة CSS المستخدَمة أو محتوى العنصر أو عنصر رئيسي. تُعرف هذه العملية باسم "التنسيق" في Chrome (والمتصفّحات المشتقة مثل Edge) وSafari. في Firefox، يُعرف هذا الإجراء باسم "إعادة التدفق"، ولكن العملية هي نفسها بشكل أساسي.

على غرار عمليات احتساب الأسلوب، تتضمّن المخاوف الفورية لتكلفة التنسيق ما يلي:

  1. عدد العناصر التي تتطلّب تنسيقًا، وهو نتيجة ثانوية لحجم DOM للصفحة.
  2. مدى تعقيد هذه التنسيقات

ملخّص

  • يؤثر التصميم تأثيرًا مباشرًا في وقت استجابة التفاعل.
  • يسري التنسيق عادةً على المستند بأكمله.
  • سيؤثّر عدد عناصر نموذج DOM في الأداء، لذا عليك تجنُّب تنشيط التنسيق كلما أمكن.
  • تجنَّب التنسيقات المتزامنة القسرية وتغييرات التنسيق المفاجئة، واقرأ قيم الأنماط ثم أجرِ تغييرات على الأنماط.

تأثيرات التنسيق على وقت استجابة التفاعل

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

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

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

تجنَّب استخدام التنسيق كلما أمكن.

عند تغيير الأنماط، يتحقّق المتصفّح لمعرفة ما إذا كان أيّ من التغييرات يتطلّب احتساب التنسيق وتعديل شجرة العرض. تتطلّب التغييرات في "السمات الهندسية"، مثل width أو height أو left أو top، جميعها تنسيقًا.

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

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

إذا لم يكن من الممكن تجنُّب التنسيق، عليك استخدام أدوات مطوّري البرامج في Chrome مرة أخرى لمعرفة الوقت الذي يستغرقه الإجراء وتحديد ما إذا كان التنسيق هو سبب حدوث ازدحام. أولاً، افتح "أدوات مطوّري البرامج"، وانتقِل إلى علامة التبويب "المخطط الزمني"، وانقر على "تسجيل"، ثم تفاعل مع موقعك الإلكتروني. عند إيقاف التسجيل، ستظهر لك تفاصيل عن أداء موقعك الإلكتروني:

أدوات المطوّر تعرِض عدة وحدات تصميم أرجوانية
أدوات مطوّري البرامج في Chrome تعرض وقتًا طويلاً في Layout (التنسيق).

عند التوغّل في عملية التتبّع في المثال السابق، نلاحظ أنّه يتم إنفاق أكثر من 28 ملي ثانية داخل التنسيق لكل لقطة، وهو وقت طويل جدًا عندما يكون لدينا 16 ملي ثانية لعرض لقطة على الشاشة في صورة متحركة. يمكنك أيضًا الاطّلاع على أنّ أدوات المطوّرين ستُعلمك بحجم الشجرة (1,618 عنصرًا في هذه الحالة)، وعدد العقد التي كانت بحاجة إلى تنسيق (5 في هذه الحالة).

تجدر الإشارة إلى أنّ النصيحة العامة هنا هي تجنُّب التنسيق كلما أمكن، ولكن قد لا يكون ذلك ممكنًا دائمًا. في الحالات التي لا يمكنك فيها تجنُّب التنسيق، يُرجى العِلم أنّ تكلفة التنسيق لها علاقة بحجم DOM. على الرغم من أنّ العلاقة بين الاثنين ليست مرتبطة بشكلٍ وثيق، فإنّ ملفات DOM الأكبر حجمًا ستترتب عليها عمومًا تكاليف أعلى للتنسيق.

تجنُّب التنسيقات المتزامنة القسرية

يتم شحن الإطار إلى الشاشة بالترتيب التالي:

استخدام flexbox كتنسيق
خطوات المعالجة

أولاً، يتم تشغيل JavaScript، ثم عمليات حسابية للأسلوب، ثم التنسيق. ومع ذلك، من الممكن إجبار المتصفّح على تنفيذ التنسيق في وقت أبكر باستخدام JavaScript. ويُعرف ذلك باسم العرض المتزامن القسري (أو إعادة التدفق القسري في بعض الأحيان).

يجب أولاً أن تضع في اعتبارك أنّه أثناء تنفيذ JavaScript، تكون جميع قيم التنسيق القديمة من الإطار السابق معروفة ومتاحة لك لإجراء طلب بحث عنها. على سبيل المثال، إذا أردت كتابة ارتفاع عنصر (لنسميه "صندوق") في بداية الإطار، يمكنك كتابة بعض الرموز البرمجية على النحو التالي:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

تحدث مشاكل إذا غيّرت أنماط المربّع قبل طلب الحصول على ارتفاعه:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

الآن، للإجابة عن سؤال الارتفاع، على المتصفّح أولاً تطبيق تغيير النمط (بسبب إضافة الفئة super-big)، ثم بعد ذلك تنفيذ التنسيق. عندها فقط سيتمكّن من عرض الارتفاع الصحيح. هذا عمل غير ضروري وقد يكون مكلفًا.

ولهذا السبب، يجب دائمًا تجميع عمليات قراءة الأنماط وتنفيذها أولاً (حيث يمكن للمتصفّح استخدام قيم تنسيق الإطار السابق)، ثم تنفيذ أي عمليات كتابة:

يمكن استخدام صيغة أكثر فعالية من الدالة السابقة على النحو التالي:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

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

تجنُّب تغييرات التصميم المفاجئة

هناك طريقة لجعل التنسيقات المتزامنة القسرية أسوأ: تنفيذ الكثير منها بشكل متتابع سريع. ألقِ نظرة على هذا الرمز:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

يكرّر هذا الرمز مجموعة من الفقرات ويضبط عرض كل فقرة ليطابق عرض عنصر يُسمى "مربّع". يبدو هذا الإجراء غير ضارّ، ولكن المشكلة هي أنّ كل تكرار للحلقة يقرأ قيمة نمط (box.offsetWidth) ثم يستخدمها على الفور لتعديل عرض الفقرة (paragraphs[i].style.width). في التكرار التالي للحلقة، على المتصفّح مراعاة حقيقة أنّ الأنماط قد تغيّرت منذ آخر طلب لـ offsetWidth (في التكرار السابق)، لذا يجب تطبيق تغييرات الأنماط وتنفيذ التنسيق. وسيحدث ذلك في كل تكرار.

لحلّ هذه المشكلة في هذا المثال، عليك قراءة القيم ثم كتابتها مرة أخرى:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

تحديد التنسيقات المتزامنة القسرية والاستخدام المفرط للذاكرة

تتضمّن "أدوات المطوّرين" إحصاءات إعادة التدفق القسري لمساعدتك في تحديد حالات التنسيقات المتزامنة القسرية (المعروفة أيضًا باسم "إعادة التدفق القسري") بسرعة:

أدوات المطوّرين تعرِض إحصاءات إعادة التدفق القسري التي تحدد دالة باسم &quot;w&quot; تتسبّب في إعادة التدفق القسري.
إحصاءات "أدوات مطوّري البرامج في Chrome" حول إعادة التدفق القسري

يمكن أيضًا تحديد التنسيقات المتزامنة القسرية في الحقل باستخدام إسناد نص Long Animation Frame API باستخدام السمة forcedStyleAndLayoutDuration.