التنسيق هو المكان الذي يحدِّد فيه المتصفّح المعلومات الهندسية للعناصر، أي حجمها وموقعها في الصفحة. سيتضمّن كل عنصر معلومات صريحة أو ضمنية عن الحجم استنادًا إلى لغة CSS المستخدَمة أو محتوى العنصر أو عنصر رئيسي. تُعرف هذه العملية باسم "التنسيق" في Chrome.
يحدِّد المتصفّح المعلومات الهندسية للعناصر في التنسيق، أي حجمها وموقعها في الصفحة. سيتضمّن كل عنصر معلومات صريحة أو ضمنية عن الحجم استنادًا إلى لغة CSS المستخدَمة أو محتوى العنصر أو عنصر رئيسي. تُعرف هذه العملية باسم "التنسيق" في 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 التي تعمل تلقائيًا على تجميع عمليات القراءة والكتابة بالنيابة عنك، ومن المفترض أن تمنعك من تشغيل التنسيقات المتزامنة التي يتم فرضها أو تعطل التنسيق عن طريق الخطأ.