كيف قمنا بتحسين أداء SmashingMag

نشرت: 2022-03-10
ملخص سريع ↬ في هذه المقالة ، سنلقي نظرة فاحصة على بعض التغييرات التي أجريناها على هذا الموقع بالذات - الذي يعمل على JAMStack مع React - لتحسين أداء الويب وتحسين مقاييس Core Web Vitals. مع بعض الأخطاء التي ارتكبناها ، وبعض التغييرات غير المتوقعة التي ساعدت في تعزيز جميع المقاييس في جميع المجالات.

كل قصة أداء ويب متشابهة ، أليس كذلك؟ يبدأ دائمًا بإصلاح موقع الويب الذي طال انتظاره. يوم يتم فيه إطلاق مشروع ، مصقول بالكامل ومحسّن بعناية ، ويحتل مرتبة عالية ومتفوقة على درجات الأداء في Lighthouse و WebPageTest. هناك احتفال وإحساس صادق بالإنجاز يسود في الهواء - ينعكس بشكل جميل في التغريدات والتعليقات والنشرات الإخبارية وخيوط سلاك.

ومع ذلك ، مع مرور الوقت ، تتلاشى الإثارة ببطء ، وتتسلل التعديلات العاجلة ، والميزات التي تشتد الحاجة إليها ، ومتطلبات العمل الجديدة. وفجأة ، قبل أن تعرف ذلك ، تزداد قاعدة الشفرة قليلاً وتشتت يجب تحميل البرامج النصية في وقت أبكر بقليل ، ويجد المحتوى الديناميكي الجديد اللامع طريقه إلى DOM من خلال الأبواب الخلفية لنصوص الطرف الرابع وضيوفهم غير المدعوين.

لقد كنا هناك في Smashing أيضًا. لا يعرف الكثير من الناس ذلك ، لكننا فريق صغير جدًا من حوالي 12 شخصًا ، يعمل الكثير منهم بدوام جزئي ومعظمهم يرتدون قبعات مختلفة في يوم معين. على الرغم من أن الأداء كان هدفنا منذ ما يقرب من عقد من الزمان ، إلا أنه لم يكن لدينا أبدًا فريق أداء متفاني.

بعد إعادة التصميم الأخيرة في أواخر عام 2017 ، كان Ilya Pukhalski في جانب JavaScript للأشياء (بدوام جزئي) ، و Michael Riethmueller من جانب CSS للأشياء (بضع ساعات في الأسبوع) ، وأنت حقًا ، يلعب ألعاب العقل باستخدام CSS الهامة ومحاولة التوفيق بين عدد قليل جدًا من الأشياء.

تظهر لقطة شاشة لمصادر الأداء نتائج Lighthouse بين 40 و 60
هذا هو المكان الذي بدأنا فيه. نظرًا لأن درجات Lighthouse تتراوح بين 40 و 60 ، قررنا معالجة الأداء (مرة أخرى) بشكل مباشر. (مصدر الصورة: Lighthouse Metrics) (معاينة كبيرة)

كما حدث ، فقدنا مسار الأداء في انشغال الروتين اليومي. كنا نصمم ونبني الأشياء ، ونصنع منتجات جديدة ، ونعيد هيكلة المكونات ، وننشر المقالات. وبحلول أواخر عام 2020 ، خرجت الأمور قليلاً عن السيطرة ، مع ظهور نتائج Lighthouse ذات اللون الأحمر المصفر ببطء في جميع المجالات. كان علينا إصلاح ذلك.

هذا هو المكان الذي كنا فيه

قد يعرف البعض منكم أننا نعمل على JAMStack ، مع تخزين جميع المقالات والصفحات كملفات Markdown ، وملفات Sass مجمعة في CSS ، وتنقسم JavaScript إلى أجزاء مع Webpack و Hugo لبناء صفحات ثابتة نعرضها بعد ذلك مباشرة من Edge CDN. في عام 2017 ، قمنا ببناء الموقع بالكامل باستخدام Preact ، ولكننا انتقلنا بعد ذلك إلى React في عام 2019 - واستخدمناه مع عدد قليل من واجهات برمجة التطبيقات للبحث والتعليقات والمصادقة والسحب.

تم إنشاء الموقع بالكامل مع وضع التحسين التدريجي في الاعتبار ، مما يعني أنه يمكنك ، عزيزي القارئ ، قراءة كل مقالة Smashing بالكامل دون الحاجة إلى تشغيل التطبيق على الإطلاق. ليس من المستغرب جدًا أيضًا - في النهاية ، لا تتغير المقالة المنشورة كثيرًا على مر السنين ، بينما تحتاج الأجزاء الديناميكية مثل مصادقة العضوية والسداد إلى تشغيل التطبيق.

يستغرق الإصدار الكامل لنشر حوالي 2500 مقالة مباشرة حوالي 6 دقائق في الوقت الحالي. أصبحت عملية البناء من تلقاء نفسها وحشًا تمامًا بمرور الوقت أيضًا ، مع حقن CSS الحرجة ، وتقسيم كود Webpack ، والإدخالات الديناميكية للإعلانات ولوحات الميزات ، وتوليد RSS (إعادة) ، واختبار A / B النهائي على الحافة.

في أوائل عام 2020 ، بدأنا بإعادة هيكلة كبيرة لمكونات تخطيط CSS. لم نستخدم أبدًا CSS-in-JS أو المكونات المصممة ، ولكن بدلاً من ذلك ، نظام جيد قائم على المكونات من وحدات Sass التي سيتم تجميعها في CSS. مرة أخرى في عام 2017 ، تم إنشاء التصميم بالكامل باستخدام Flexbox وأعيد بناؤه باستخدام CSS Grid و CSS Custom Properties في منتصف عام 2019. ومع ذلك ، احتاجت بعض الصفحات إلى معاملة خاصة بسبب البقع الإعلانية الجديدة ولوحات المنتجات الجديدة. لذلك بينما كان التخطيط يعمل ، لم يكن يعمل بشكل جيد ، وكان من الصعب الحفاظ عليه.

بالإضافة إلى ذلك ، كان لابد من تغيير رأس التنقل الرئيسي لاستيعاب المزيد من العناصر التي أردنا عرضها ديناميكيًا. بالإضافة إلى ذلك ، أردنا إعادة تشكيل بعض المكونات المستخدمة بشكل متكرر والمستخدمة في جميع أنحاء الموقع ، وكان CSS المستخدم هناك بحاجة إلى بعض المراجعة أيضًا - صندوق الرسائل الإخبارية هو الجاني الأكثر بروزًا. لقد بدأنا بإعادة هيكلة بعض المكونات باستخدام CSS الأول من نوعه ولكننا لم نصل أبدًا إلى النقطة التي تم استخدامها باستمرار عبر الموقع بأكمله.

كانت المشكلة الأكبر هي حزمة جافا سكريبت الكبيرة التي - ليس من المستغرب جدًا - أن تحظر الخيط الرئيسي لمئات من المللي ثانية. قد تبدو حزمة JavaScript كبيرة في غير محلها في مجلة تنشر مقالات فقط ، ولكن في الواقع ، هناك الكثير من البرامج النصية التي تحدث خلف الكواليس.

لدينا حالات مختلفة من المكونات للعملاء المصدقين وغير المعتمدين. بمجرد تسجيل الدخول ، نرغب في عرض جميع المنتجات بالسعر النهائي ، وعندما تضيف كتابًا إلى عربة التسوق ، نريد الاحتفاظ بعربة التسوق التي يمكن الوصول إليها بضغطة زر - بغض النظر عن الصفحة التي تتصفحها. يجب أن يأتي الإعلان بسرعة دون التسبب في حدوث تغييرات مزعجة في التخطيط ، وينطبق الشيء نفسه على لوحات المنتجات الأصلية التي تسلط الضوء على منتجاتنا. بالإضافة إلى عامل خدمة يخزن جميع الأصول الثابتة مؤقتًا ويخدمها لتكرار العروض ، جنبًا إلى جنب مع الإصدارات المخزنة مؤقتًا من المقالات التي زارها القارئ بالفعل.

لذلك كان يجب أن تحدث كل هذه البرمجة النصية في وقت ما ، وكانت تستنزف تجربة القراءة على الرغم من أن النص كان يأتي في وقت متأخر جدًا. بصراحة ، كنا نعمل بجد على الموقع والمكونات الجديدة دون مراقبة الأداء عن كثب (وكان لدينا بعض الأشياء الأخرى التي يجب وضعها في الاعتبار لعام 2020). جاءت نقطة التحول بشكل غير متوقع. أدار هاري روبرتس دروسه الرئيسية (الممتازة) في أداء الويب كورشة عمل عبر الإنترنت معنا ، وطوال ورشة العمل بأكملها ، كان يستخدم Smashing كمثال من خلال تسليط الضوء على المشكلات التي لدينا واقتراح حلول لهذه المشكلات إلى جانب الأدوات والإرشادات المفيدة.

طوال ورشة العمل ، كنت أقوم بتدوين الملاحظات بجدية وإعادة زيارة قاعدة الرموز. في وقت ورشة العمل ، كانت درجات Lighthouse لدينا 60-68 على الصفحة الرئيسية ، وحوالي 40-60 على صفحات المقالات - ومن الواضح أنها أسوأ على الهاتف المحمول. بمجرد انتهاء ورشة العمل ، بدأنا العمل.

تحديد الاختناقات

غالبًا ما نميل إلى الاعتماد على درجات معينة لفهم مدى جودة أدائنا ، ولكن في كثير من الأحيان لا تقدم الدرجات الفردية صورة كاملة. كما أشار ديفيد إيست ببلاغة في مقالته ، فإن أداء الويب ليس قيمة واحدة ؛ إنه توزيع. حتى إذا كانت تجربة الويب تمثل أداءً شاملاً محسنًا بشكل كبير وشامل ، فلا يمكن أن تكون سريعة فقط. قد يكون سريعًا لبعض الزوار ، لكنه في النهاية سيكون أبطأ (أو بطيئًا) لبعض الآخرين.

أسباب ذلك عديدة ، ولكن الأهم هو الاختلاف الكبير في ظروف الشبكة وأجهزة الجهاز في جميع أنحاء العالم. في كثير من الأحيان لا يمكننا التأثير حقًا على هذه الأشياء ، لذلك علينا التأكد من أن تجربتنا تستوعبها بدلاً من ذلك.

في الأساس ، تتمثل مهمتنا إذن في زيادة نسبة التجارب السريعة وتقليل نسبة التجارب البطيئة. ولكن من أجل ذلك ، نحتاج إلى الحصول على صورة مناسبة لما هو التوزيع في الواقع. الآن ، ستوفر أدوات التحليلات وأدوات مراقبة الأداء هذه البيانات عند الحاجة ، لكننا نظرنا بشكل خاص في CrUX ، تقرير تجربة مستخدم Chrome. يُنشئ CrUX نظرة عامة على توزيعات الأداء بمرور الوقت ، مع حركة المرور التي تم جمعها من مستخدمي Chrome. تتعلق الكثير من هذه البيانات بـ Core Web Vitals التي أعلنت عنها Google في عام 2020 ، والتي تساهم أيضًا في Lighthouse ويتم الكشف عنها.

تُظهر إحصائيات أكبر محتوى محتوى (LCP) انخفاضًا هائلاً في الأداء بين مايو وسبتمبر في عام 2020
توزيع أداء Largest Contentful Paint في عام 2020. انخفض الأداء بشكل كبير بين مايو وسبتمبر. البيانات من CrUX. (معاينة كبيرة)

لاحظنا أن أداؤنا تراجع بشكل كبير على مدار العام ، مع انخفاضات خاصة في شهري أغسطس وسبتمبر. بمجرد أن نرى هذه الرسوم البيانية ، يمكننا أن ننظر إلى الوراء في بعض العلاقات العامة التي دفعناها مباشرة إلى الوراء لدراسة ما حدث بالفعل.

لم يستغرق الأمر بعض الوقت لمعرفة أننا في هذه الأوقات تقريبًا أطلقنا شريط تنقل جديدًا مباشرًا. اعتمد شريط التنقل - المستخدم في جميع الصفحات - على JavaScript لعرض عناصر التنقل في قائمة عند النقر أو النقر ، ولكن جزء JavaScript منه تم تجميعه بالفعل داخل حزمة app.js. لتحسين Time To Interactive ، قررنا استخراج نص التنقل من الحزمة وتقديمه بشكل مضمّن.

في نفس الوقت تقريبًا ، انتقلنا من ملف CSS هام (قديم) تم إنشاؤه يدويًا إلى نظام مؤتمت كان يولد CSS مهمًا لكل قالب - الصفحة الرئيسية ، المقالة ، صفحة المنتج ، الحدث ، لوحة الوظائف ، وما إلى ذلك - و CSS الهامة المضمنة أثناء وقت البناء. ومع ذلك ، لم ندرك حقًا مقدار ثقل CSS الحرج الذي تم إنشاؤه تلقائيًا. كان علينا استكشافها بمزيد من التفصيل.

وأيضًا في نفس الوقت تقريبًا ، كنا نعدل تحميل خط الويب ، في محاولة لدفع خطوط الويب بقوة أكبر باستخدام تلميحات الموارد مثل التحميل المسبق. يبدو أن هذا رد فعل عكسيًا لجهودنا في الأداء ، نظرًا لأن خطوط الويب كانت تؤخر عرض المحتوى ، حيث تم تحديد أولوياتها بشكل مبالغ فيه بجوار ملف CSS الكامل.

الآن ، أحد الأسباب الشائعة للانحدار هو التكلفة الباهظة لـ JavaScript ، لذلك نظرنا أيضًا في Webpack Bundle Analyzer وخريطة طلب Simon Hearne للحصول على صورة مرئية لتبعيات JavaScript الخاصة بنا. بدا الأمر صحيًا تمامًا في البداية.

خريطة ذهنية بصرية لتتبعيات JavaScript
لا شيء مبتكر حقًا: لا يبدو أن خريطة الطلب مفرطة في البداية. (معاينة كبيرة)

كانت هناك بعض الطلبات الواردة إلى CDN ، وهي خدمة الموافقة على ملفات تعريف الارتباط Cookiebot و Google Analytics ، بالإضافة إلى خدماتنا الداخلية لخدمة لوحات المنتجات والإعلانات المخصصة. لا يبدو أن هناك الكثير من الاختناقات - حتى نظرنا عن كثب.

في عمل الأداء ، من الشائع إلقاء نظرة على أداء بعض الصفحات المهمة - على الأرجح الصفحة الرئيسية وعلى الأرجح بعض صفحات المقالات / المنتجات. ومع ذلك ، على الرغم من وجود صفحة رئيسية واحدة فقط ، فقد يكون هناك الكثير من صفحات المنتجات المختلفة ، لذلك نحتاج إلى اختيار الصفحات التي تمثل جمهورنا.

في الواقع ، نظرًا لأننا ننشر عددًا لا بأس به من المقالات ذات الشفرات الثقيلة والمثقلة بالتصميم على SmashingMag ، فقد تراكمت على مر السنين حرفيًا آلاف المقالات التي تحتوي على صور GIF ثقيلة ومقتطفات تعليمات برمجية مميزة ومضمنات CodePen وفيديو / صوت التضمينات والخيوط المتداخلة للتعليقات التي لا تنتهي أبدًا.

عندما تم جمعها معًا ، كان العديد منها يتسبب في حدوث انفجار في حجم DOM جنبًا إلى جنب مع العمل المفرط للخيط الرئيسي - مما أدى إلى إبطاء التجربة على آلاف الصفحات. ناهيك عن أنه مع وجود الإعلان في مكانه الصحيح ، تم إدخال بعض عناصر DOM في وقت متأخر من دورة حياة الصفحة مما تسبب في سلسلة من عمليات إعادة حساب الأنماط وإعادة الطلاء - وهي أيضًا مهام باهظة الثمن يمكن أن تنتج مهامًا طويلة.

لم يكن كل هذا يظهر في الخريطة التي أنشأناها لصفحة مقالة خفيفة للغاية في الرسم البياني أعلاه. لذلك اخترنا أثقل الصفحات التي لدينا - الصفحة الرئيسية القوية ، والأطول ، والصفحة التي تحتوي على العديد من مقاطع الفيديو المضمنة ، والصفحة التي تحتوي على العديد من تضمينات CodePen - وقررنا تحسينها قدر الإمكان. بعد كل شيء ، إذا كانت سريعة ، فيجب أن تكون الصفحات التي تحتوي على تضمين CodePen واحد أسرع أيضًا.

مع وضع هذه الصفحات في الاعتبار ، بدت الخريطة مختلفة قليلاً. لاحظ الخط السميك الضخم المتجه إلى مشغل Vimeo و Vimeo CDN ، مع 78 طلبًا من مقال Smashing.

خريطة ذهنية مرئية تعرض مشكلات الأداء خاصة في المقالات التي استخدمت الكثير من مقاطع الفيديو و / أو تضمين مقاطع الفيديو
في بعض صفحات المقالات ، بدا الرسم البياني مختلفًا. خاصة مع وجود الكثير من التعليمات البرمجية أو تضمين مقاطع الفيديو ، كان الأداء ينخفض ​​بشكل كبير. لسوء الحظ ، تحتوي العديد من مقالاتنا عليها. (معاينة كبيرة)

لدراسة التأثير على الخيط الرئيسي ، تعمقنا في لوحة الأداء في DevTools. وبشكل أكثر تحديدًا ، كنا نبحث عن المهام التي تدوم أطول من 50 مللي ثانية (مظللة بمستطيل أحمر في الزاوية العلوية اليمنى) والمهام التي تحتوي على أنماط إعادة الحساب (شريط أرجواني). يشير الأول إلى تكلفة تنفيذ JavaScript باهظة الثمن ، في حين أن الأخير قد يكشف عن إبطال النمط الناتج عن الحقن الديناميكي للمحتوى في DOM و CSS دون المستوى الأمثل. أعطانا هذا بعض المؤشرات القابلة للتنفيذ من أين نبدأ. على سبيل المثال ، اكتشفنا بسرعة أن تحميل خط الويب الخاص بنا كان له تكلفة إعادة رسم كبيرة ، في حين أن أجزاء JavaScript لا تزال ثقيلة بما يكفي لمنع الخيط الرئيسي.

لقطة شاشة من لوحة الأداء في DevTools تُظهر أجزاء JavaScript التي كانت لا تزال ثقيلة بما يكفي لمنع الخيط الرئيسي
دراسة لوحة الأداء في DevTools. كان هناك عدد قليل من المهام الطويلة ، استغرقت أكثر من 50 مللي ثانية وحجبت السلسلة الرئيسية. (معاينة كبيرة)

كخط أساس ، نظرنا عن كثب إلى Core Web Vitals ، في محاولة للتأكد من أننا نحقق نتائج جيدة عبر كل منهم. لقد اخترنا التركيز بشكل خاص على الأجهزة المحمولة البطيئة - مع سرعة نقل 3G بطيئة و 400 مللي ثانية RTT و 400 كيلوبت في الثانية ، فقط لنكون في الجانب المتشائم من الأشياء. ليس من المستغرب إذن أن Lighthouse لم يكن سعيدًا جدًا بموقعنا أيضًا ، حيث قدم درجات حمراء ثابتة تمامًا لأثقل المقالات ، ويشكو بلا كلل من JavaScript و CSS والصور خارج الشاشة وأحجامها غير المستخدمة.

لقطة شاشة لبيانات Lighthouse توضح الفرص والمدخرات المقدرة
لم يكن Lighthouse سعيدًا بشكل خاص بأداء بعض الصفحات أيضًا. هذا هو واحد مع الكثير من تضمين الفيديو. (معاينة كبيرة)

بمجرد حصولنا على بعض البيانات أمامنا ، يمكننا التركيز على تحسين صفحات المقالات الثلاثة الأثقل ، مع التركيز على CSS المهمة (وغير الحرجة) وحزمة JavaScript والمهام الطويلة وتحميل خطوط الويب وتحولات التخطيط والجهات الخارجية أشباه. لاحقًا ، قمنا أيضًا بمراجعة قاعدة التعليمات البرمجية لإزالة الشفرة القديمة واستخدام ميزات المتصفح الحديثة الجديدة. يبدو أن هناك الكثير من العمل الذي ينتظرنا ، وبالفعل كنا مشغولين للغاية للأشهر القادمة.

تحسين ترتيب الأصول في <head>

ومن المفارقات أن أول شيء بحثناه لم يكن مرتبطًا ارتباطًا وثيقًا بجميع المهام التي حددناها أعلاه. في ورشة الأداء ، أمضى هاري وقتًا طويلاً في شرح ترتيب الأصول في <head> لكل صفحة ، مما يشير إلى أن تقديم المحتوى الهام بسرعة يعني أن تكون إستراتيجيًا للغاية ويقظًا بشأن كيفية ترتيب الأصول في الكود المصدري .

الآن لا ينبغي أن يكون ذلك بمثابة كشف كبير عن أن CSS المهم مفيد لأداء الويب. ومع ذلك ، فقد كان مفاجئًا نوعًا ما مقدار الاختلاف في ترتيب جميع الأصول الأخرى - تلميحات الموارد ، والتحميل المسبق لخط الويب ، والنصوص المتزامنة وغير المتزامنة ، و CSS الكامل والبيانات الوصفية.

لقد قلبنا <head> بالكامل رأسًا على عقب ، ووضعنا CSS الحرجة قبل جميع البرامج النصية غير المتزامنة وجميع الأصول المحملة مسبقًا مثل الخطوط والصور وما إلى ذلك. لقد قمنا بتقسيم الأصول التي سنقوم بالتوصيل بها مسبقًا أو التحميل المسبق حسب النموذج و نوع الملف ، بحيث سيتم طلب الصور المهمة وإبراز البنية وتضمينات الفيديو في وقت مبكر فقط لنوع معين من المقالات والصفحات.

بشكل عام ، قمنا بتنسيق الترتيب بعناية في <head> ، وقللنا عدد الأصول المحملة مسبقًا التي كانت تتنافس على النطاق الترددي ، وركزنا على الحصول على CSS الصحيح بشكل صحيح. إذا كنت ترغب في التعمق في بعض الاعتبارات الحرجة بترتيب <head> ، فإن Harry يسلط الضوء عليها في المقالة حول CSS وأداء الشبكة. هذا التغيير وحده جعلنا نحقق 3-4 نقاط لايتهاوس في جميع المجالات.

الانتقال من CSS الآلي الحرج إلى CSS اليدوي الحرج

كان تحريك علامات <head> جزءًا بسيطًا من القصة. كان الأمر الأكثر صعوبة هو إنشاء ملفات CSS الهامة وإدارتها. في عام 2017 ، قمنا يدويًا بتصميم CSS المهمة لكل قالب ، من خلال جمع كل الأنماط المطلوبة لعرض أول 1000 بكسل في الارتفاع عبر جميع عروض الشاشة. كانت هذه بالطبع مهمة مرهقة وغير ملهمة إلى حد ما ، ناهيك عن مشكلات الصيانة لترويض عائلة كاملة من ملفات CSS الهامة وملف CSS كامل.

لذلك نظرنا في خيارات أتمتة هذه العملية كجزء من روتين البناء. لم يكن هناك نقص حقًا في الأدوات المتاحة ، لذلك اختبرنا القليل منها وقررنا إجراء بعض الاختبارات. لقد تمكنا من إعدادها وتشغيلها بسرعة كبيرة. بدا الناتج جيدًا بما يكفي لعملية مؤتمتة ، لذلك بعد بعض تعديلات التكوين ، قمنا بتوصيله ودفعناه إلى الإنتاج. حدث ذلك في الفترة من يوليو إلى أغسطس من العام الماضي ، وهو ما تم تصوره بشكل جيد في الارتفاع المفاجئ وانخفاض الأداء في بيانات CrUX أعلاه. ظللنا نذهب إلى التهيئة ذهابًا وإيابًا ، وغالبًا ما نواجه مشكلات في أشياء بسيطة مثل الإضافة في أنماط معينة أو إزالة الأنماط الأخرى. على سبيل المثال ، أنماط مطالبة الموافقة على ملفات تعريف الارتباط التي لا يتم تضمينها بالفعل في الصفحة ما لم يتم تهيئة البرنامج النصي لملف تعريف الارتباط.

في أكتوبر ، أدخلنا بعض التغييرات الرئيسية في التخطيط على الموقع ، وعند النظر في CSS المهم ، واجهنا نفس المشكلات تمامًا مرة أخرى - كانت النتيجة التي تم إنشاؤها مطولة تمامًا ، ولم تكن تمامًا كما أردنا . لذا ، كتجربة في أواخر أكتوبر ، جمعنا جميعًا نقاط قوتنا لإعادة النظر في نهج CSS المهم ودراسة مدى صغر حجم CSS المهم يدويًا . أخذنا نفسًا عميقًا وقضينا أيامًا حول أداة تغطية الشفرة على الصفحات الرئيسية. قمنا بتجميع قواعد CSS يدويًا وإزالة التكرارات والشفرات القديمة في كلا المكانين - CSS المهم و CSS الرئيسي. لقد كانت عملية تنظيف تمس الحاجة إليها بالفعل ، حيث أن العديد من الأساليب التي تمت كتابتها في 2017-2018 أصبحت قديمة على مر السنين.

نتيجة لذلك ، انتهى بنا الأمر بثلاثة ملفات CSS مهمة مصنوعة يدويًا ، وثلاثة ملفات أخرى قيد التنفيذ حاليًا:

  • الهامة-homepage-manual.css (8.2 كيلوبايت ، Brotlified)
  • حرجة-article-manual.css (8 كيلوبايت ، Brotlified)
  • الهامة-articles-manual.css (6 كيلوبايت ، Brotlified)
  • حرجة-books-manual.css ( عمل يتعين القيام به )
  • الأحداث الحرجة manual.css ( العمل الذي يتعين القيام به )
  • الحرجة-وظيفة-board-manual.css ( العمل الذي يتعين القيام به )

الملفات مضمنة في رأس كل قالب ، وفي الوقت الحالي يتم تكرارها في حزمة CSS متجانسة تحتوي على كل شيء تم استخدامه على الإطلاق (أو لم يعد مستخدمًا بالفعل) على الموقع. في الوقت الحالي ، نتطلع إلى تقسيم حزمة CSS الكاملة إلى بضع حزم CSS ، لذلك لن يقوم قارئ المجلة بتنزيل الأنماط من لوحة الوظائف أو صفحات الكتاب ، ولكن عند الوصول إلى هذه الصفحات سيحصل على عرض سريع باستخدام CSS المهمة واحصل على بقية CSS لتلك الصفحة بشكل غير متزامن - فقط على تلك الصفحة.

من المسلم به أن ملفات CSS المهمة المصنوعة يدويًا لم تكن أصغر حجمًا: لقد قللنا حجم ملفات CSS الهامة بحوالي 14٪ . ومع ذلك ، فقد قاموا بتضمين كل ما نحتاجه بالترتيب الصحيح من الأعلى إلى النهاية دون التكرارات والأنماط المتجاوزة. يبدو أن هذه خطوة في الاتجاه الصحيح ، وقد أعطتنا دفعة أخرى من Lighthouse بمقدار 3-4 نقاط أخرى. كنا نحرز تقدما.

تغيير تحميل خط الويب

مع font-display في متناول أيدينا ، يبدو أن تحميل الخط يمثل مشكلة في الماضي. لسوء الحظ ، هذا ليس صحيحًا تمامًا في حالتنا. يبدو أنكم ، أيها القراء الأعزاء ، تزورون عددًا من المقالات في مجلة Smashing Magazine. تعود أيضًا بشكل متكرر إلى الموقع لقراءة مقال آخر - ربما بعد بضع ساعات أو أيام ، أو ربما بعد أسبوع. كانت إحدى المشكلات التي واجهتنا مع font-display المستخدمة عبر الموقع هي أنه بالنسبة للقراء الذين تنقلوا بين المقالات كثيرًا ، لاحظنا الكثير من الومضات بين الخط الاحتياطي وخط الويب (وهو ما لا ينبغي أن يحدث عادةً كما هو الحال مع الخطوط. مخبأ بشكل صحيح).

لم يكن ذلك تجربة مستخدم لائقة ، لذلك نظرنا في الخيارات. في Smashing ، نستخدم محرفين رئيسيين - Mija للعناوين و Elena لنسخ الجسم. تأتي Mija في وزنين (عادي وجريء) ، بينما تأتي Elena بثلاثة أوزان (عادي ، مائل ، غامق). لقد أسقطنا كتاب Elena's Bold Italic منذ سنوات أثناء إعادة التصميم لمجرد أننا استخدمناه في بضع صفحات فقط. قمنا بتقسيم الخطوط الأخرى عن طريق إزالة الأحرف غير المستخدمة ونطاقات Unicode.

يتم تعيين مقالاتنا في الغالب في نص ، لذلك اكتشفنا أنه في معظم الأوقات على الموقع ، يكون Largest Contentful Paint هو إما الفقرة الأولى من النص في مقال أو صورة المؤلف. هذا يعني أننا بحاجة إلى توخي مزيد من العناية لضمان ظهور الفقرة الأولى بسرعة في خط احتياطي ، مع التغيير بأمان إلى خط الويب مع الحد الأدنى من التدفقات المعاد تدفقها.

ألق نظرة فاحصة على تجربة التحميل الأولية للصفحة الأولى (تم الإبطاء ثلاث مرات):

كان لدينا أربعة أهداف أساسية عند التوصل إلى حل:

  1. في الزيارة الأولى ، اعرض النص على الفور بخط احتياطي ؛
  2. مطابقة مقاييس الخطوط للخطوط الاحتياطية وخطوط الويب لتقليل تحولات التخطيط ؛
  3. قم بتحميل جميع خطوط الويب بشكل غير متزامن وقم بتطبيقها جميعًا مرة واحدة (بحد أقصى 1 إعادة تدفق) ؛
  4. في الزيارات اللاحقة ، اعرض كل النص مباشرة في خطوط الويب (بدون أي وميض أو إعادة تدفق).

في البداية ، حاولنا بالفعل استخدام عرض الخط: تبديل على font-face . يبدو أن هذا هو الخيار الأبسط ، ومع ذلك ، كما ذكرنا أعلاه ، فإن بعض القراء سيزورون عددًا من الصفحات ، لذلك انتهى بنا الأمر مع الكثير من الخفقان مع الخطوط الستة التي كنا نعرضها في جميع أنحاء الموقع. أيضًا ، مع عرض الخط وحده ، لم نتمكن من تجميع الطلبات أو إعادة الطلاء.

كانت هناك فكرة أخرى تتمثل في عرض كل شيء بخط احتياطي في الزيارة الأولية ، ثم طلب جميع الخطوط وتخزينها مؤقتًا بشكل غير متزامن ، وفقط في الزيارات اللاحقة يتم تسليم خطوط الويب مباشرة من ذاكرة التخزين المؤقت. تكمن المشكلة في هذا النهج في أن عددًا من القراء يأتون من محركات البحث ، وسيشاهد بعضهم على الأقل تلك الصفحة الواحدة - ولم نرغب في عرض مقال بخط النظام وحده.

فماذا بعد ذلك؟

منذ عام 2017 ، كنا نستخدم منهج Two-Stage-Render لتحميل خط الويب والذي يصف بشكل أساسي مرحلتين من العرض: إحداهما تحتوي على مجموعة فرعية من خطوط الويب ، والأخرى بمجموعة كاملة من أوزان الخطوط. مرة أخرى في اليوم ، أنشأنا الحد الأدنى من مجموعات فرعية من Mija Bold و Elena Regular والتي كانت أكثر الأوزان استخدامًا على الموقع. تتضمن كلتا المجموعتين الفرعيتين فقط الأحرف اللاتينية وعلامات الترقيم والأرقام وبعض الأحرف الخاصة. كانت هذه الخطوط ( ElenaInitial.woff2 و MijaInitial.woff2 ) صغيرة جدًا في الحجم - غالبًا ما يتراوح حجمها بين 10-15 كيلو بايت. نحن نخدمهم في المرحلة الأولى من تقديم الخط ، ونعرض الصفحة بأكملها في هذين الخطين.

CLS ناتج عن وميض خطوط الويب
CLS الناجم عن وميض خطوط الويب (الظلال الموجودة أسفل صور المؤلف تتحرك بسبب تغيير الخط). تم إنشاؤه باستخدام Layout Shift GIF Generator. (معاينة كبيرة)

نقوم بذلك من خلال واجهة برمجة تطبيقات تحميل الخطوط التي تعطينا معلومات حول الخطوط التي تم تحميلها بنجاح والتي لم يتم تحميلها بعد. خلف الكواليس ، يحدث ذلك عن طريق إضافة فئة .wf-تحميل-stage1 إلى الجسم ، مع أنماط تجعل المحتوى في تلك الخطوط:

 .wf-loaded-stage1 article, .wf-loaded-stage1 promo-box, .wf-loaded-stage1 comments { font-family: ElenaInitial,sans-serif; } .wf-loaded-stage1 h1, .wf-loaded-stage1 h2, .wf-loaded-stage1 .btn { font-family: MijaInitial,sans-serif; }

نظرًا لأن ملفات الخطوط صغيرة جدًا ، نأمل أن تمر عبر الشبكة بسرعة كبيرة. بعد ذلك ، نظرًا لأن القارئ يمكن أن يبدأ فعليًا في قراءة مقال ، نقوم بتحميل أوزان كاملة للخطوط بشكل غير متزامن ، ونضيف .wf-portable-stage2 إلى النص الأساسي :

 .wf-loaded-stage2 article, .wf-loaded-stage2 promo-box, .wf-loaded-stage2 comments { font-family: Elena,sans-serif; } .wf-loaded-stage2 h1, .wf-loaded-stage2 h2, .wf-loaded-stage2 .btn { font-family: Mija,sans-serif; }

لذلك عند تحميل صفحة ، سيحصل القراء على مجموعة فرعية صغيرة من خطوط الويب بسرعة أولاً ، ثم ننتقل إلى مجموعة الخطوط الكاملة. الآن ، بشكل افتراضي ، تحدث هذه التبديل بين الخطوط الاحتياطية وخطوط الويب بشكل عشوائي ، بناءً على كل ما يأتي أولاً عبر الشبكة. قد يبدو ذلك مزعجًا للغاية لأنك بدأت في قراءة مقال. لذا بدلاً من ترك الأمر للمتصفح ليقرر وقت تبديل الخطوط ، نقوم بتجميع عمليات إعادة الطلاء ، مما يقلل من تأثير إعادة التدفق إلى الحد الأدنى.

 /* Loading web fonts with Font Loading API to avoid multiple repaints. With help by Irina Lipovaya. */ /* Credit to initial work by Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // If the Font Loading API is supported... // (If not, we stick to fallback fonts) if ("fonts" in document) { // Create new FontFace objects, one for each font let ElenaRegular = new FontFace( "Elena", "url(/fonts/ElenaWebRegular/ElenaWebRegular.woff2) format('woff2')" ); let ElenaBold = new FontFace( "Elena", "url(/fonts/ElenaWebBold/ElenaWebBold.woff2) format('woff2')", { weight: "700" } ); let ElenaItalic = new FontFace( "Elena", "url(/fonts/ElenaWebRegularItalic/ElenaWebRegularItalic.woff2) format('woff2')", { style: "italic" } ); let MijaBold = new FontFace( "Mija", "url(/fonts/MijaBold/Mija_Bold-webfont.woff2) format('woff2')", { weight: "700" } ); // Load all the fonts but render them at once // if they have successfully loaded let loadedFonts = Promise.all([ ElenaRegular.load(), ElenaBold.load(), ElenaItalic.load(), MijaBold.load() ]).then(result => { result.forEach(font => document.fonts.add(font)); document.documentElement.classList.add('wf-loaded-stage2'); // Used for repeat views sessionStorage.foutFontsStage2Loaded = true; }).catch(error => { throw new Error(`Error caught: ${error}`); }); }

ومع ذلك ، ماذا لو لم تأتي أول مجموعة فرعية صغيرة من الخطوط عبر الشبكة بسرعة؟ لقد لاحظنا أن هذا يبدو أنه يحدث في كثير من الأحيان أكثر مما نرغب في ذلك. في هذه الحالة ، بعد انتهاء مهلة 3 ثوانٍ ، تعود المتصفحات الحديثة إلى خط النظام (في مجموعة الخطوط لدينا سيكون Arial) ، ثم قم بالتبديل إلى ElenaInitial أو MijaInitial ، فقط ليتم التبديل إلى Elena أو Mija الكامل لاحقًا. . أنتج ذلك القليل من الوميض في تذوقنا. كنا نفكر في إزالة عرض المرحلة الأولى فقط للشبكات البطيئة في البداية (عبر Network Information API) ، ولكن بعد ذلك قررنا إزالتها تمامًا.

لذلك في أكتوبر ، أزلنا المجموعات الفرعية تمامًا ، جنبًا إلى جنب مع المرحلة المتوسطة. عندما يتم تنزيل جميع أوزان كل من خطوط Elena و Mija بنجاح بواسطة العميل وتكون جاهزة للتطبيق ، نبدأ المرحلة 2 ونعيد رسم كل شيء مرة واحدة. ولجعل تدفقات التدفق أقل وضوحًا ، أمضينا بعض الوقت في مطابقة الخطوط الاحتياطية وخطوط الويب . كان هذا يعني في الغالب تطبيق أحجام خطوط مختلفة قليلاً وارتفاعات خطوط للعناصر المرسومة في الجزء الأول المرئي من الصفحة.

لذلك ، استخدمنا font-style-matcher و (ahem، ahem) عددًا قليلاً من الأرقام السحرية. وهذا أيضًا هو السبب الذي دفعنا في البداية إلى استخدام -apple-system و Arial كخطوط احتياطية عالمية ؛ يبدو أن سان فرانسيسكو (التي تم تقديمها عبر نظام -Apple ) أجمل قليلاً من Arial ، ولكن إذا لم تكن متوفرة ، فقد اخترنا استخدام Arial لمجرد أنها منتشرة على نطاق واسع عبر معظم أنظمة التشغيل.

في CSS ، سيبدو كما يلي:

 .article__summary { font-family: -apple-system,Arial,BlinkMacSystemFont,Roboto Slab,Droid Serif,Segoe UI,Ubuntu,Cantarell,Georgia,sans-serif; font-style: italic; /* Warning: magic numbers ahead! */ /* San Francisco Italic and Arial Italic have larger x-height, compared to Elena */ font-size: 0.9213em; line-height: 1.487em; } .wf-loaded-stage2 .article__summary { font-family: Elena,sans-serif; font-size: 1em; /* Original font-size for Elena Italic */ line-height: 1.55em; /* Original line-height for Elena Italic */ }

عملت هذا بشكل جيد إلى حد ما. نحن نعرض النص على الفور ، وتظهر خطوط الويب على الشاشة مجمعة ، مما يؤدي بشكل مثالي إلى إعادة تدفق واحد بالضبط في العرض الأول ، وعدم التدفق كليًا في العروض اللاحقة.

بمجرد تنزيل الخطوط ، نقوم بتخزينها في ذاكرة التخزين المؤقت لعامل الخدمة. في الزيارات اللاحقة ، نتحقق أولاً مما إذا كانت الخطوط موجودة بالفعل في ذاكرة التخزين المؤقت. إذا كانت كذلك ، فإننا نسترجعها من ذاكرة التخزين المؤقت لعامل الخدمة ونطبقها على الفور. وإذا لم يكن الأمر كذلك ، فنحن نبدأ من جديد بالنسخ الاحتياطي-ويب-الخط-سويتشرو .

قلل هذا الحل من عدد التدفقات المعاد تدفقها إلى الحد الأدنى (واحد) على الاتصالات السريعة نسبيًا ، مع الاحتفاظ أيضًا بالخطوط بشكل ثابت وموثوق في ذاكرة التخزين المؤقت. في المستقبل ، نأمل مخلصين في استبدال الأرقام السحرية بأرقام تعديل f. ربما سيفخر زاك ليثرمان.

تحديد وتفكيك متجانسة JS

عندما درسنا الموضوع الرئيسي في لوحة أداء DevTools ، عرفنا بالضبط ما يتعين علينا القيام به. كانت هناك ثماني مهام طويلة تستغرق ما بين 70 و 580 مللي ثانية ، مما يحظر الواجهة ويجعلها غير مستجيبة. بشكل عام ، كانت هذه النصوص هي الأكثر تكلفة:

  • uc.js ، برمجة نصية لملف تعريف الارتباط (70 مللي ثانية)
  • عمليات إعادة حساب الأنماط الناتجة عن ملف full.css الوارد (176 مللي ثانية) (لا يحتوي CSS المهم على أنماط أقل من ارتفاع 1000 بكسل عبر جميع إطارات العرض)
  • نصوص إعلانية تعمل على حدث تحميل لإدارة اللوحات وعربة التسوق وما إلى ذلك + عمليات إعادة حساب النمط (276 مللي ثانية)
  • تبديل خط الويب ، إعادة حساب النمط (290 مللي ثانية)
  • تقييم app.js (580 مللي ثانية)

ركزنا على تلك التي كانت الأكثر ضررًا أولاً - أو على ما يقال ، المهام الطويلة.

لقطة شاشة مأخوذة من DevTools تعرض عمليات التحقق من صحة النمط للصفحة الأولى للمجلة المحطمة
في الجزء السفلي ، يعرض Devtools إبطال النمط - أثر تبديل الخط على 549 عنصرًا كان يجب إعادة رسمها. ناهيك عن التحولات في التخطيط التي كانت تسببها. (معاينة كبيرة)

الأول كان يحدث بسبب عمليات إعادة حساب التخطيط الباهظة بسبب تغيير الخطوط (من الخط الاحتياطي إلى خط الويب) ، مما تسبب في عمل إضافي يزيد عن 290 مللي ثانية (على كمبيوتر محمول سريع واتصال سريع). بإزالة المرحلة الأولى من تحميل الخط وحده ، تمكنا من استعادة حوالي 80 مللي ثانية. لم تكن جيدة بما فيه الكفاية على الرغم من أنها كانت تتجاوز ميزانية 50 مللي ثانية. لذلك بدأنا بالحفر بشكل أعمق.

كان السبب الرئيسي وراء حدوث عمليات إعادة الحساب هو مجرد الاختلافات الهائلة بين الخطوط الاحتياطية وخطوط الويب. من خلال مطابقة ارتفاع السطر وأحجام الخطوط الاحتياطية وخطوط الويب ، تمكنا من تجنب العديد من المواقف عندما يلتف سطر من النص على سطر جديد في الخط الاحتياطي ، ولكن بعد ذلك يصبح أصغر قليلاً ويتناسب مع السطر السابق ، تسبب في تغيير كبير في هندسة الصفحة بأكملها ، وبالتالي تغييرات كبيرة في التخطيط. لقد لعبنا أيضًا مع letter-spacing word-spacing ، لكن ذلك لم يسفر عن نتائج جيدة.

من خلال هذه التغييرات ، تمكنا من قطع 50-80 مللي ثانية أخرى ، لكننا لم نتمكن من تقليلها إلى أقل من 120 مللي ثانية دون عرض المحتوى بخط احتياطي وعرض المحتوى في خط الويب بعد ذلك. من الواضح أنه يجب أن يؤثر بشكل كبير على الزائرين لأول مرة فقط حيث سيتم عرض مشاهدات الصفحة اللاحقة مع الخطوط المسترجعة مباشرة من ذاكرة التخزين المؤقت لعامل الخدمة ، دون إعادة التدفقات المكلفة بسبب تبديل الخط.

بالمناسبة ، من المهم جدًا أن نلاحظ أنه في حالتنا ، لاحظنا أن معظم المهام الطويلة لم تكن ناتجة عن JavaScript ضخم ، ولكن بدلاً من ذلك عن طريق عمليات إعادة حساب التخطيط وتحليل CSS ، مما يعني أننا بحاجة إلى إجراء القليل من CSS cleaning, especially watching out for situations when styles are overwritten. In some way, it was good news because we didn't have to deal with complex JavaScript issues that much. However, it turned out not to be straightforward as we are still cleaning up the CSS this very day. We were able to remove two Long Tasks for good, but we still have a few outstanding ones and quite a way to go. Fortunately, most of the time we aren't way above the magical 50ms threshold.

The much bigger issue was the JavaScript bundle we were serving, occupying the main thread for a whopping 580ms. Most of this time was spent in booting up app.js which contains React, Redux, Lodash, and a Webpack module loader. The only way to improve performance with this massive beast was to break it down into smaller pieces. So we looked into doing just that.

With Webpack, we've split up the monolithic bundle into smaller chunks with code-splitting , about 30Kb per chunk. We did some package.json cleansing and version upgrade for all production dependencies, adjusted the browserlistrc setup to address the two latest browser versions, upgraded to Webpack and Babel to the latest versions, moved to Terser for minification, and used ES2017 (+ browserlistrc) as a target for script compilation.

We also used BabelEsmPlugin to generate modern versions of existing dependencies. Finally, we've added prefetch links to the header for all necessary script chunks and refactored the service worker, migrating to Workbox with Webpack (workbox-webpack-plugin).

A screenshot showing JavaScript chunks affecting performance with each running no longer than 40ms on the main thread
JavaScript chunks in action, with each running no longer than 40ms on the main thread. (معاينة كبيرة)

Remember when we switched to the new navigation back in mid-2020, just to see a huge performance penalty as a result? The reason for it was quite simple. While in the past the navigation was just static plain HTML and a bit of CSS, with the new navigation, we needed a bit of JavaScript to act on opening and closing of the menu on mobile and on desktop. That was causing rage clicks when you would click on the navigation menu and nothing would happen, and of course, had a penalty cost in Time-To-Interactive scores in Lighthouse.

We removed the script from the bundle and extracted it as a separate script . Additionally, we did the same thing for other standalone scripts that were used rarely — for syntax highlighting, tables, video embeds and code embeds — and removed them from the main bundle; instead, we granularly load them only when needed.

Performance stats for the smashing magazine front page showing the function call for nav.js that happened right after a monolithic app.js bundle had been executed
Notice that the function call for nav.js is happening after a monolithic app.js bundle is executed. That's not quite right. (معاينة كبيرة)

However, what we didn't notice for months was that although we removed the navigation script from the bundle, it was loading after the entire app.js bundle was evaluated, which wasn't really helping Time-To-Interactive (see image above). We fixed it by preloading nav.js and deferring it to execute in the order of appearance in the DOM, and managed to save another 100ms with that operation alone. By the end, with everything in place we were able to bring the task to around 220ms.

A screenshot of the the Long task reduced by almost 200ms
By prioritizing the nav.js script, we were able to reduce the Long task by almost 200ms. (معاينة كبيرة)

We managed to get some improvement in place, but still have quite a way to go, with further React and Webpack optimizations on our to-do list. At the moment we still have three major Long Tasks — font switch (120ms), app.js execution (220ms) and style recalculations due to the size of full CSS (140ms). For us, it means cleaning up and breaking up the monolithic CSS next.

It's worth mentioning that these results are really the best-scenario- results. On a given article page we might have a large number of code embeds and video embeds, along with other third-party scripts and customer's browser extensions that would require a separate conversation.

Dealing With 3rd-Parties

Fortunately, our third-party scripts footprint (and the impact of their friends' fourth-party-scripts) wasn't huge from the start. But when these third-party scripts accumulated, they would drive performance down significantly. This goes especially for video embedding scripts , but also syntax highlighting, advertising scripts, promo panels scripts and any external iframe embeds.

Obviously, we defer all of these scripts to start loading after the DOMContentLoaded event, but once they finally come on stage, they cause quite a bit of work on the main thread. This shows up especially on article pages, which are obviously the vast majority of content on the site.

The first thing we did was allocating proper space to all assets that are being injected into the DOM after the initial page render. It meant width and height for all advertising images and the styling of code snippets. We found out that because all the scripts were deferred, new styles were invalidating existing styles, causing massive layout shifts for every code snippet that was displayed. We fixed that by adding the necessary styles to the critical CSS on the article pages.

We've re-established a strategy for optimizing images (preferably AVIF or WebP — still work in progress though). All images below the 1000px height threshold are natively lazy-loaded (with <img loading=lazy> ), while the ones on the top are prioritized ( <img loading=eager> ). The same goes for all third-party embeds.

We replaced some dynamic parts with their static counterparts — eg while a note about an article saved for offline reading was appearing dynamically after the article was added to the service worker's cache, now it appears statically as we are, well, a bit optimistic and expect it to be happening in all modern browsers.

As of the moment of writing, we're preparing facades for code embeds and video embeds as well. Plus, all images that are offscreen will get decoding=async attribute, so the browser has a free reign over when and how it loads images offscreen, asynchronously and in parallel.

A screenshot of the main front page of smashing magazine being highlighted by the Diagnostics CSS tool for each image that does not have a width/height attribute
Diagnostics CSS in use: highlighting images that don't have width/height attributes, or are served in legacy formats. (معاينة كبيرة)

To ensure that our images always include width and height attributes, we've also modified Harry Roberts' snippet and Tim Kadlec's diagnostics CSS to highlight whenever an image isn't served properly. It's used in development and editing but obviously not in production.

One technique that we used frequently to track what exactly is happening as the page is being loaded, was slow-motion loading .

First, we've added a simple line of code to the diagnostics CSS, which provides a noticeable outline for all elements on the page.

* { outline: 3px solid red }
* { outline: 3px solid red } 
A screenshot of an article published on smashing magazine with red lines on the layout to help check the stability and rendering on the page
A quick trick to check the stability of the layout, by adding * { outline: 3px red } and observing the boxes as the browser is rendering the page. (معاينة كبيرة)

Then we record a video of the page loaded on a slow and fast connection. Then we rewatch the video by slowing down the playback and moving back and forward to identify where massive layout shifts happen.

Here's the recording of a page being loaded on a fast connection:

Recording for the loading of the page with an outline applied, to observe layout shifts.

And here's the recording of a recording being played to study what happens with the layout:

Auditing the layout shifts by rewatching a recording of the site loading in slow motion, watching out for height and width of content blocks, and layout shifts.

By auditing the layout shifts this way, we were able to quickly notice what's not quite right on the page, and where massive recalculation costs are happening. As you probably have noticed, adjusting the line-height and font-size on headings might go a long way to avoid large shifts.

With these simple changes alone, we were able to boost performance score by a whopping 25 Lighthouse points for the video-heaviest article, and gain a few points for code embeds.

Enhancing The Experience

We've tried to be quite strategic in pretty much everything from loading web fonts to serving critical CSS. However, we've done our best to use some of the new technologies that have become available last year.

We are planning on using AVIF by default to serve images on SmashingMag, but we aren't quite there yet, as many of our images are served from Cloudinary (which already has beta support for AVIF), but many are directly from our CDN yet we don't really have a logic in place just yet to generate AVIFs on the fly. That would need to be a manual process for now.

We're lazy rendering some of the offset components of the page with content-visibility: auto . For example, the footer, the comments section, as well as the panels way below the first 1000px height threshold, are all rendered later after the visible portion of each page has been rendered.

لقد لعبنا قليلاً مع link rel="prefetch" وحتى link rel="prerender" (NoPush Preetch) بعض أجزاء الصفحة التي يُحتمل جدًا استخدامها لمزيد من التنقل - على سبيل المثال ، الجلب المسبق للأصول لأول مقالات على الصفحة الأولى (لا تزال قيد المناقشة).

نقوم أيضًا بتحميل صور المؤلف مسبقًا لتقليل أكبر رسم محتوى ، وبعض الأصول الرئيسية المستخدمة في كل صفحة ، مثل صور القط الراقصة (للتنقل) والظل المستخدم لجميع صور المؤلف. ومع ذلك ، لا يتم تحميلها جميعًا مسبقًا إلا إذا كان القارئ على شاشة أكبر (> 800 بكسل) ، على الرغم من أننا نتطلع إلى استخدام واجهة برمجة تطبيقات معلومات الشبكة بدلاً من ذلك لتكون أكثر دقة.

لقد قللنا أيضًا حجم CSS الكامل وجميع ملفات CSS الهامة عن طريق إزالة التعليمات البرمجية القديمة ، وإعادة بناء عدد من المكونات ، وإزالة خدعة ظل النص التي كنا نستخدمها لتحقيق تسطير مثالي مع مزيج من زخرفة النص وتخطي - سمك - سمك - زخرفة النص ( أخيرًا!).

العمل الذي يتعين القيام به

لقد أمضينا وقتًا طويلاً جدًا في العمل على جميع التغييرات الطفيفة والكبيرة على الموقع. لقد لاحظنا تحسينات كبيرة جدًا على سطح المكتب وزيادة ملحوظة على الهاتف المحمول. في وقت كتابة هذا التقرير ، تسجل مقالاتنا في المتوسط ​​ما بين 90 و 100 درجة Lighthouse على سطح المكتب ، وحوالي 65-80 على الهاتف المحمول .

نتيجة Lighthouse على عروض سطح المكتب بين 90 و 100
نقاط الأداء على سطح المكتب. تم بالفعل تحسين الصفحة الرئيسية بشكل كبير. (معاينة كبيرة)
نتيجة Lighthouse في عروض الجوال بين 65 و 80
على الهاتف المحمول ، نادرًا ما نصل إلى درجة Lighthouse التي تزيد عن 85. لا تزال المشكلات الرئيسية هي وقت التفاعل ووقت الحظر الإجمالي. (معاينة كبيرة)

من الواضح أن سبب ضعف النتيجة على الهاتف المحمول هو ضعف وقت التفاعل وضعف وقت الحظر الإجمالي بسبب تمهيد التطبيق وحجم ملف CSS الكامل. لذلك لا يزال هناك بعض العمل الذي يتعين القيام به هناك.

بالنسبة للخطوات التالية ، فإننا نتطلع حاليًا إلى مزيد من تقليل حجم CSS ، ونقوم على وجه التحديد بتقسيمها إلى وحدات ، على غرار JavaScript ، وتحميل بعض أجزاء CSS (مثل الخروج أو لوحة الوظائف أو الكتب / الكتب الإلكترونية) فقط عندما ضروري.

نستكشف أيضًا خيارات المزيد من تجارب التجميع على الهاتف المحمول لتقليل تأثير أداء app.js على الرغم من أنه يبدو غير تافه في الوقت الحالي. أخيرًا ، سنبحث في بدائل لحل ملف تعريف الارتباط الفوري الخاص بنا ، وإعادة بناء حاوياتنا باستخدام clamp() ، واستبدال تقنية نسبة الحشو السفلي مع نسبة aspect-ratio والنظر في تقديم أكبر عدد ممكن من الصور في AVIF.

هذا كل شيء ، أيها الناس!

نأمل أن تكون دراسة الحالة الصغيرة هذه مفيدة لك ، وربما هناك أسلوب أو أسلوبان يمكنك تطبيقهما على مشروعك على الفور. في النهاية ، يتمحور الأداء حول مجموع كل التفاصيل الصغيرة الدقيقة ، والتي عند إضافتها تؤدي إلى حدوث أو كسر تجربة عميلك.

بينما نحن ملتزمون جدًا بتحسين الأداء ، فإننا نعمل أيضًا على تحسين إمكانية الوصول ومحتوى الموقع. لذا إذا اكتشفت أي شيء غير صحيح تمامًا أو أي شيء يمكننا القيام به لتحسين مجلة Smashing ، فالرجاء إخبارنا في التعليقات على هذه المقالة.

أخيرًا ، إذا كنت ترغب في البقاء على اطلاع دائم بمقالات مثل هذه ، فالرجاء الاشتراك في النشرة الإخبارية عبر البريد الإلكتروني للحصول على نصائح ويب ودية ، وأشياء جيدة ، وأدوات ومقالات ، ومجموعة مختارة موسمية من Smashing cats.