التخطيط هو المكان الذي يكتشف فيه المتصفح المعلومات الهندسية للعناصر - حجمها وموقعها في الصفحة. وسيتضمّن كل عنصر معلومات واضحة أو ضمنية حول المقاس بناءً على لغة CSS التي تم استخدامها أو محتوى العنصر أو العنصر الأصلي. تُعرف هذه العملية باسم "التنسيق" في Chrome.
التخطيط هو المكان الذي يكتشف فيه المتصفح المعلومات الهندسية للعناصر: حجمها وموقعها في الصفحة. وسيتضمّن كل عنصر معلومات واضحة أو ضمنية حول المقاس بناءً على لغة CSS التي تم استخدامها أو محتوى العنصر أو العنصر الأصلي. وتُسمى هذه العملية Layout في Chrome (والمتصفحات المشتقة مثل Edge) وSafari. يُطلق عليها في Firefox اسم Reflow، ولكن العملية هي نفسها بشكل فعال.
على غرار العمليات الحسابية للأنماط، فإن المخاوف الفورية المتعلقة بتكلفة التخطيط هي:
- عدد العناصر التي تتطلب تنسيقًا، وهو منتج ثانوي لحجم DOM للصفحة.
- مدى تعقيد تلك التخطيطات.
ملخّص
- للتخطيط تأثير مباشر في وقت استجابة التفاعل
- يتم عادةً تحديد نطاق التنسيق على المستند بأكمله.
- سيؤثر عدد عناصر DOM في الأداء، لذا يجب تجنُّب تشغيل التنسيق كلما أمكن ذلك.
- تجنب التخطيطات المتزامنة والإجبارية على التنسيق، ويمكنك قراءة قيم الأنماط ثم إجراء تغييرات على النمط.
تأثيرات التنسيق على وقت استجابة التفاعل
عندما يتفاعل المستخدم مع الصفحة، يجب أن تكون تلك التفاعلات بأسرع ما يمكن. يُعرف مقدار الوقت الذي يستغرقه اكتمال التفاعل، والذي ينتهي عندما يعرض المتصفّح الإطار التالي لعرض نتائج التفاعل، باسم وقت استجابة التفاعل. هذا جانب من أداء الصفحة يقيسه مقياس مدى استجابة الصفحة لتفاعلات المستخدم.
يُعرف الوقت الذي يستغرقه المتصفّح لعرض الإطار التالي استجابةً لتفاعل المستخدم باسم تأخير العرض التقديمي للتفاعل. الهدف من التفاعل هو تقديم ملاحظات مرئية من أجل إرسال إشارة للمستخدم بحدوث شيء ما، ويمكن أن تتضمن التحديثات المرئية قدرًا من عمل التخطيط من أجل تحقيق هذا الهدف.
للحفاظ على INP لموقعك الإلكتروني بأدنى قدر ممكن، من المهم تجنُّب تنسيق المحتوى قدر الإمكان. إذا لم يكن من الممكن تجنب التخطيط تمامًا، فمن المهم تقييد هذا التنسيق حتى يتمكن المتصفح من عرض الإطار التالي بسرعة.
تجنب التخطيط كلما أمكن ذلك
عند تغيير الأنماط، يتحقّق المتصفّح مما إذا كان أي من التغييرات يتطلب احتساب التنسيق، ولكي يتم تعديل شجرة العرض هذه. تتطلب التغييرات التي يتم إجراؤها على "الخصائص الهندسية" مثل العرض أو الارتفاع أو اليمين أو الأعلى تنسيقًا.
.box {
width: 20px;
height: 20px;
}
/**
* Changing width and height
* triggers layout.
*/
.box--expanded {
width: 200px;
height: 350px;
}
يتم تحديد نطاق التنسيق دائمًا على المستند بأكمله. إذا كان لديك العديد من العناصر، سيستغرق الأمر وقتًا طويلاً لمعرفة مواقعها وأبعادها جميعًا.
إذا لم تتمكّن من تجنُّب التنسيق، من المهم استخدام "أدوات مطوري البرامج في Chrome" مرة أخرى لمعرفة المدة التي تستغرقها العملية وتحديد ما إذا كان التنسيق هو سبب المشكلة. أولاً، افتح "أدوات مطوري البرامج" وانتقِل إلى علامة التبويب "المخطّط الزمني" ثم انقر على "تسجيل" وتفاعَل مع موقعك الإلكتروني. عند إيقاف التسجيل، ستشاهد تفصيلًا لأداء موقعك الإلكتروني:
![أدوات مطوّري البرامج تعرض وقتًا طويلاً في قسم "التنسيق"](https://web.dev/static/articles/avoid-large-complex-layouts-and-layout-thrashing/image/devtools-showing-long-i-9f35099ab9d22.png?authuser=6&hl=ar)
عند التعمق في آثار الأنشطة الواردة في المثال أعلاه، نرى أنه يتم قضاء ما يزيد عن 28 مللي ثانية داخل التخطيط لكل إطار، وهو ما يكون مرتفعًا جدًا عندما يكون لدينا 16 ملي ثانية لعرض إطار على الشاشة في أحد الرسوم المتحركة. يمكنك أيضًا أن ترى أنّ أدوات مطوري البرامج ستطلعك على حجم الشجرة (1,618 عنصرًا في هذه الحالة)، وعدد العُقد التي كانت بحاجة إلى التنسيق (5 في هذه الحالة).
يجب مراعاة أنّ النصائح العامة الواردة هنا هي تجنُّب التنسيق كلما أمكن ذلك، ولكن لا يمكن دائمًا تجنُّب التنسيق. في الحالات التي لا يمكنك فيها تجنب التخطيط، عليك معرفة أن تكلفة التخطيط لها علاقة بحجم نموذج العناصر في المستند (DOM). على الرغم من عدم ارتباط العلاقة بين الاثنين بإحكام، ستكبّد وحدات DOM الأكبر حجمًا بشكل عام تكاليف تنسيق أعلى.
تجنُّب التنسيقات المتزامنة التي تم فرضها
يتم تنفيذ عملية شحن إطار إلى الشاشة على النحو التالي:
![استخدام flexbox كتخطيط.](https://web.dev/static/articles/avoid-large-complex-layouts-and-layout-thrashing/image/using-flexbox-layout-0c9955c54a296.jpg?authuser=6&hl=ar)
أولاً، يتم تشغيل 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.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 التي تعمل تلقائيًا على تجميع عمليات القراءة والكتابة نيابةً عنك، ومن المفترض أن تمنعك من تشغيل تنسيقات متزامنة مفروضة أو تعطل التنسيق عن طريق الخطأ.
صورة رئيسية من قناة Unspark، بقلم هال غيتوود