هندسة
انزياح تخطيط لا يوجد إلا أثناء تدفق الـ HTML
By Clasify Team · · 5 min read
سجّلت صفحتنا الرئيسية 73 في Lighthouse بقيمة Cumulative Layout Shift تساوي 0.351 — في المنطقة الحمراء بوضوح. والمُربك أن الصفحة النهائية لا تتحرك إطلاقًا. افتحها، وراقبها، وغيّر مقاس النافذة: لا شيء يقفز. وكل الأسباب المعتادة كانت مُعالَجة سلفًا؛ الصور تحمل أبعادها، ولا يُحقَن أي شريط بعد التحميل، ولا يوجد خط ويب يحلّ متأخرًا.
كان الانزياح حقيقيًا، وغائبًا تمامًا عن التخطيط النهائي، لأنه يحدث بينما الـ HTML لا يزال في الطريق.
الـ CLS خاصية التحميل، لا خاصية التخطيط
يقيس Cumulative Layout Shift مقدار حركة المحتوى المرئي خلال عمر الصفحة. ومن السهل قراءة ذلك بوصفه "هل يتحرك تصميمي النهائي؟" ثم الذهاب للبحث عن المشتبهين المعتادين: وسائط بلا أبعاد، وعناصر تُحقَن متأخرة، وخطوط.
لكن المتصفح لا ينتظر وصول مستندك كي يبدأ التخطيط. إنه يحلّل ويرسم تدريجيًا. وإن كان الـ HTML كبيرًا، رسم المتصفح نسخة جزئية من صفحتك ثم أعاد تخطيطها كلما وصل المزيد. وكل واحدة من إعادات التخطيط هذه تُحتسب.
هذه هي الفجوة التي وقعنا فيها. صفحاتنا التسويقية تُبنى مسبقًا وقت البناء كي يحصل الزاحف والزائر لأول مرة على HTML حقيقي بدل هيكل فارغ ينتظر JavaScript. والصفحة الرئيسية نحو 300 كيلوبايت من ذلك. وهذا مفيد لأشياء كثيرة — لكنه يفتح نافذة، عرضها مئات الميلي ثانية، يخطّط فيها المتصفح مستندًا لا يزال ينمو.
صنف واحد، ومعظم النتيجة
كان قسم الـ hero مكتوبًا هكذا:
min-h-[90vh] flex items-center
توسيط رأسي للمحتوى داخل صندوق بارتفاع أدنى. قرار معقول، وبالنسبة للصفحة النهائية معطَّل تمامًا: قِسنا ارتفاع المحتوى عند كل مقاس من 360×640 إلى 1920×1080، وهو دائمًا أطول من 90vh. فالتوسيط لا أثر له البتة في التخطيط الذي تراه فعلًا.
لكن أثره هائل بينما يتدفق المستند. فبعد تحليل أول 54 بكسل من المحتوى، يوسّط المتصفح ذلك الجزء داخل صندوق الـ 90vh ويرسمه عند y=420. ثم يصل الباقي — 812 بكسل أخرى — فيصبح توسيط المحتوى الأطول عند y=128. يقطع الصندوق 292 بكسل صعودًا، ويتحرك معه كل ما تحته.
تغيير `items-center` إلى `items-start` أزال 0.348 من أصل 0.351. صار المحتوى ينمو نزولًا من حافة علوية ثابتة، وهو ما يفعله بصريًا على أي حال.
لن ترى هذا على جهازك المحلي
هذا هو الجزء الجدير بالنقل، أيًا كان إطار العمل الذي تستخدمه.
خادم التطوير المحلي يسلّمك المستند فورًا. فنافذة التدفق المسبِّبة للانزياح تكاد لا توجد، ومن ثَمّ لا يتكرر الخلل، وأي "إصلاح" تجرّبه يبدو ناجحًا. وسيظل Lighthouse على الموقع المنشور مخالفًا لك.
انتهى بنا الأمر إلى كتابة proxy صغير يقدّم الـ HTML الإنتاجي الحقيقي على دفعات مُبطَّأة، فيصل المستند عبر نافذة واقعية. وهذا جعل الرقم قابلًا للتكرار حتى أربع خانات عشرية، فتحوّل العمل من تخمين إلى قياس: غيّر شيئًا واحدًا، أعد التشغيل، وانظر هل تحرك الرقم.
إن كنت تطارد انزياحًا لا تستطيع تكراره، فكرّر **التسليم** أولًا. الخلل عادةً ليس في الوسوم، بل في التوقيت.
الـ 0.14 الأخيرة كانت دائرتين غير مرئيتين
بعد إزالة التوسيط بقي نحو 0.14 بلا تفسير، مصدرها توهّجان خلفيان كبيران ومموّهان.
هذان العنصران موضوعان نسبةً إلى قسم الـ hero — أحدهما مثبَّت إلى حافته السفلى والآخر إلى منتصفه الرأسي. فموضعهما دالة على ارتفاع القسم، وارتفاع القسم هو بالضبط الشيء غير المستقر أثناء التدفق. كانا يُرسمان ثم يتحركان، مرارًا.
والتفصيل الذي يجعل المشكلة قابلة للحل: العناصر الشفافة تمامًا مستثناة من حساب انزياح التخطيط. وهذان لم يكونا شفافين تمامًا — كانا يُرسمان بعتامة نحو 5%، وهي كافية تمامًا للاحتساب.
لذلك نُبقيهما عند عتامة صفر طوال النافذة غير المستقرة ثم نُظهرهما تدريجيًا بعدها. حركة عتامة فقط، فلا يمكنها هي نفسها أن تُزيح شيئًا، وتبدأ بعد استقرار المستند — أي أن التوهّجين يظهران مباشرةً في موضعهما النهائي. والـ hero النهائي مطابق بكسلًا ببكسل لما كان عليه، وقد تحققنا من ذلك على عرض سطح المكتب والهاتف.
نزل الـ CLS إلى 0.003 وارتفعت درجة الأداء إلى 90.
ما سنقوله لفريق آخر
- تعامل مع الـ CLS بوصفه سؤالًا عن عملية التحميل لا عن التصميم. سؤال "هل تتحرك الصفحة النهائية؟" هو السؤال الخاطئ، وسيرسلك للبحث في المكان الخاطئ.
- المستندات الكبيرة المبنية مسبقًا أو المُصيَّرة على الخادم مقايضة. وهي غالبًا المقايضة الصحيحة — فوجود محتوى حقيقي في الاستجابة الأولى يستحق الكثير — لكنها توسّع النافذة التي يحدث فيها تخطيط جزئي، فيتصرف التوسيط والمواضع النسبية إلى مقاس الشاشة على غير ما تتوقع.
- أي عنصر موضوع نسبةً إلى حاوية لا يزال ارتفاعها ينمو هو انزياح مؤجَّل. وهذا يشمل عناصر زخرفية لا يعدّها أحد محتوى.
- كرّر الشبكة لا الصفحة فقط. الإصلاح الذي تحقّقتَ منه محليًا ليس متحقَّقًا منه.
- أبقِ مسارًا لتقليل الحركة. حركتنا مثبَّتة على الظهور الكامل لمن طلب حركة أقل، حتى لا يتركه الإصلاح أمام عنصر غير مرئي إلى الأبد.
لم يتطلب أي من هذا إعادة كتابة. كان 26 سطرًا في ملفين. والجزء المكلف لم يكن الإصلاح، بل بناء طريقة لرؤية المشكلة بأمانة.
ننفّذ هذا النوع من العمل لفرق أخرى ضمن تطوير الويب، وكيف بنينا Clasify يغطي المعمارية الأوسع.
كل المقالات