التخطيط هو المكان الذي يكتشف فيه المتصفح المعلومات الهندسية للعناصر - حجمها وموقعها في الصفحة. سيحتوي كل عنصر على معلومات تحديد حجم واضحة أو ضمنية استنادًا إلى لغة CSS التي تمّ استخدامها أو محتوى العنصر أو عنصر رئيسي. تُسمى هذه العملية "التخطيط" في Chrome.
التخطيط هو المكان الذي يكتشف فيه المتصفح المعلومات الهندسية للعناصر: حجمها وموقعها في الصفحة. سيحتوي كل عنصر على معلومات تحديد حجم واضحة أو ضمنية استنادًا إلى لغة CSS التي تمّ استخدامها أو محتوى العنصر أو عنصر رئيسي. تُسمى هذه العملية "تخطيط" في Chrome (والمتصفحات المشتقة مثل Edge) وSafari. في فايرفوكس، يُطلق على ذلك اسم Reflow، ولكن العملية هي نفسها بشكل فعال.
على غرار حسابات النمط، فإن المخاوف الفورية المتعلقة بتكلفة التخطيط هي:
- عدد العناصر التي تتطلب تنسيقًا، وهو منتج ثانوي لحجم 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، تكون جميع قيم التنسيق القديمة من الإطار السابق معروفة ومتاحة للاستعلام عنها. لذلك، على سبيل المثال، إذا كنت تريد كتابة ارتفاع عنصر ما (لنسميه "box") في بداية الإطار، فيمكنك كتابة بعض التعليمات البرمجية مثل هذا:
// 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". يبدو الأمر غير مؤذٍ بدرجة كافية، ولكن تكمن المشكلة في أن كل تكرار في التكرار الحلقي يقرأ قيمة نمط (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 التي تعمل تلقائيًا على تجميع القراءات والكتابة نيابةً عنك، ومن المفترض أن تمنعك من تشغيل التنسيقات المتزامنة التي تفرضها أو تعطيل التنسيق عن طريق الخطأ.
صورة رئيسية من UnLaunch، للفنان هال غيتوود