التخطيط هو المكان الذي يكتشف فيه المتصفح المعلومات الهندسية للعناصر - حجمها وموقعها في الصفحة. سيتضمّن كل عنصر معلومات صريحة أو ضمنية عن الحجم استنادًا إلى لغة CSS المستخدَمة أو محتوى العنصر أو عنصر رئيسي. تُعرف هذه العملية باسم "التنسيق" في Chrome.
يحدِّد المتصفّح المعلومات الهندسية للعناصر في التنسيق، أي حجمها وموقعها في الصفحة. سيتضمّن كل عنصر معلومات صريحة أو ضمنية عن الحجم استنادًا إلى لغة CSS المستخدَمة أو محتوى العنصر أو عنصر رئيسي. وتُسمى هذه العملية Layout في Chrome (والمتصفحات المشتقة مثل Edge) وSafari. في Firefox، يُعرف هذا الإجراء باسم "إعادة التدفق"، ولكن العملية هي نفسها بشكل أساسي.
على غرار العمليات الحسابية للأنماط، فإن المخاوف الفورية المتعلقة بتكلفة التخطيط هي:
- عدد العناصر التي تتطلّب تنسيقًا، وهو نتيجة ثانوية لحجم DOM للصفحة.
- مدى تعقيد هذه التنسيقات
ملخّص
- يؤثر التصميم تأثيرًا مباشرًا في وقت استجابة التفاعل.
- يسري التنسيق عادةً على المستند بأكمله.
- سيؤثّر عدد عناصر نموذج DOM في الأداء، لذا عليك تجنُّب تنشيط التنسيق كلما أمكن.
- تجنَّب التنسيقات المتزامنة القسرية وتغييرات التنسيق المفاجئة، واقرأ قيم الأنماط ثم أجرِ تغييرات على الأنماط.
تأثيرات التنسيق على وقت استجابة التفاعل
عندما يتفاعل المستخدم مع الصفحة، يجب أن تكون هذه التفاعلات سريعة قدر الإمكان. يُعرف وقت استجابة التفاعل بأنه الوقت المستغرَق لإكمال التفاعل، وينتهي عندما يعرض المتصفّح اللقطة التالية لعرض نتائج التفاعل. هذا جانب من أداء الصفحة يقيسه مقياس مدى استجابة الصفحة لتفاعلات المستخدم.
يُعرف الوقت الذي يستغرقه المتصفّح لعرض الإطار التالي استجابةً لتفاعل المستخدم باسم تأخير العرض التقديمي للتفاعل. يهدف التفاعل إلى تقديم ملاحظات مرئية للإشارة إلى المستخدم بأنّه حدث شيء ما، ويمكن أن تتضمّن التعديلات المرئية قدرًا من العمل على التنسيق لتحقيق هذا الهدف.
للحفاظ على INP لموقعك الإلكتروني بأدنى قدر ممكن، من المهم تجنُّب تنسيق المحتوى قدر الإمكان. إذا لم يكن من الممكن تجنُّب التنسيق تمامًا، من المهم الحد من عمل التنسيق هذا حتى يتمكّن المتصفّح من عرض اللقطة التالية بسرعة.
تجنب التخطيط كلما أمكن ذلك
عند تغيير الأنماط، يتحقّق المتصفّح لمعرفة ما إذا كان أيّ من التغييرات يتطلّب احتساب التنسيق وتعديل شجرة العرض. تتطلّب التغييرات على "السمات الهندسية"، مثل العرض أو الارتفاع أو اليسار أو الأعلى، جميعها تنسيقًا.
.box {
width: 20px;
height: 20px;
}
/**
* Changing width and height
* triggers layout.
*/
.box--expanded {
width: 200px;
height: 350px;
}
يكون التنسيق في معظم الأحيان مخصّصًا للمستند بأكمله. إذا كان لديك الكثير من العناصر، سيستغرق الأمر وقتًا طويلاً لمعرفة مواقعها وأبعادها جميعًا.
إذا لم يكن من الممكن تجنُّب التنسيق، عليك استخدام أدوات مطوّري البرامج في Chrome مرة أخرى لمعرفة الوقت الذي يستغرقه الإجراء وتحديد ما إذا كان التنسيق هو سبب حدوث عرقلة. أولاً، افتح أدوات مطوّري البرامج، وانتقِل إلى علامة التبويب "المخطط الزمني"، وانقر على "تسجيل"، ثم تفاعل مع موقعك الإلكتروني. عند إيقاف التسجيل، ستظهر لك تفاصيل عن أداء موقعك الإلكتروني:
عند التوغّل في عملية التتبّع في المثال أعلاه، نرى أنّه يتم إنفاق أكثر من 28 ملي ثانية داخل التنسيق لكل لقطة، وهو وقت طويل جدًا عندما يكون لدينا 16 ملي ثانية لعرض لقطة على الشاشة في صورة متحركة. يمكنك أيضًا الاطّلاع على أنّ أدوات المطوّرين ستُعلمك بحجم الشجرة (1,618 عنصرًا في هذه الحالة)، وعدد العقد التي كانت بحاجة إلى تنسيق (5 في هذه الحالة).
تجدر الإشارة إلى أنّ النصيحة العامة هنا هي تجنُّب التنسيق كلما أمكن ذلك، ولكن قد لا يكون ذلك ممكنًا دائمًا. في الحالات التي لا يمكنك فيها تجنب التخطيط، عليك معرفة أن تكلفة التخطيط لها علاقة بحجم نموذج العناصر في المستند (DOM). على الرغم من أنّ العلاقة بين الاثنين ليست مرتبطة بشكلٍ وثيق، فإنّ ملفات DOM الأكبر حجمًا ستترتب عليها بشكلٍ عام تكاليف أعلى للتنسيق.
تجنُّب التنسيقات المتزامنة التي تم فرضها
يتم شحن الإطار إلى الشاشة بالترتيب التالي:
أولاً، يتم تشغيل 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`;
}
}
إذا كنت تريد ضمان الأمان، ننصحك باستخدام FastDOM الذي يُجمِّع عمليات القراءة والكتابة تلقائيًا نيابةً عنك، ومن المفترض أن يمنعك من تشغيل التنسيقات المتزامنة القسرية أو إيقاف التنسيقات عن طريق الخطأ.