تحسين أداء الويب
هندسة الأداء في Next.js
عمل على مؤشرات Core Web Vitals في تطبيقات Next.js القائمة - يُقاس ببيانات الميدان، ويُعالَج عند سببه المعماري، ويُدافَع عنه في خطّ التكامل المستمر كي تبقى الأرقام مُصلَحة.
لعمل الأداء نمط فشل معروف: يُنفق فريق دورة تطوير كاملة على التحسين، فتقفز الدرجة، ثم تعود بعد ستة أسابيع إلى ما كانت عليه. لم يكن في الإصلاحات خطأ، بل كان الناقص آليةً تحفظها.
كيف يسير العمل
القياس أولًا، من الميدان. نبدأ ببيانات تقرير Chrome UX وبيانات المراقبة لديكم، مقسّمة حسب فئة الجهاز وقالب المسار. فالقول إنّ قيمة LCP عند الشريحة 75 تبلغ 3.4 ثانية في صفحات المنتجات على الهاتف عبارة قابلة للتنفيذ، أما «درجتنا في Lighthouse هي 62» فليست كذلك.
إيجاد السبب لا العَرَض. بطء LCP هو قرار عرض، أو استراتيجية خطوط، أو خطّ معالجة صور، أو طرف ثالث يحجب المسار الحرج. وضعف INP يعود في الغالب إلى حجم وشكل الـ JavaScript الذي يُرطَّب فوق الطيّة. نتتبّع كل مؤشّر وصولًا إلى القرار المحدّد على مستوى الـ commit الذي أنتجه.
الإصلاح على المستوى المعماري. نقل مكوّن عبر حدّ الخادم، وإزالة جلب بيانات من جهة العميل كان ينبغي أن يكون عرضًا على الخادم، واستبدال مكتبة تواريخ بحجم 300 كيلوبايت، وتقسيم مسار يُرسل حزمة لوحة الإدارة كاملةً إلى زوّار مجهولين. هذه هي التغييرات التي تصمد.
الدفاع عن النتيجة. ينتهي كل عمل بميزانية أداء مفروضة في خطّ التكامل المستمر. أي pull request يدفع حزمة المسار فوق سقفها يُسقط الفحص. الميزانية هي المُخرَج الحقيقي، والدرجة أثر جانبي.
لماذا تعود الدرجة إلى ما كانت
لأن الدرجة نتيجة، ولا أحد يملك نتيجة. أما الميزانية فرقمٌ في ملف، تفحصه آلة، عند كل pull request، وتُفشل بناء من تجاوزها وهو ما يزال يذكر ما الذي غيّره.
هذا الفرق هو كل السبب في أن المشروع الثاني لا يحدث عادةً. التحسين بلا ميزانية
قرضٌ على الربع القادم: المكوّن نفسه يأخذ use client لا يحتاجه، والمكتبة نفسها
تُستورد في المستوى الأعلى، ويعود التراجع التزامًا معقولًا بعد آخر، ممّن لم يكن
لديهم سبيل إلى المعرفة.
ولكي تنفع الميزانية عليها أن تقيس الشيء الصحيح. سقفٌ على مُخرَج البناء كلّه لا يقول شيئًا، لأن البناء يحوي شيفرة لا يُحمّلها أي مسار. ما نضعه سقفٌ لكل مسار على حجم JavaScript المضغوط الذي يطلبه HTML ذلك المسار فعلًا - وهو الرقم الذي يدفعه الزائر.
أين يذهب الوقت فعلًا
عبر المشاريع التي ننفّذها، تتجمّع الأسباب في أنماط:
حدّ عميل مرسوم عاليًا أكثر من اللازم. توجيه واحد على تخطيط يُرسل ذلك التخطيط وكل ما تحته إلى كل زائر على كل مسار أسفله. وهو أشيع سبب لسوء INP في تطبيق Next.js، وغالبًا ما يكون سطرًا واحدًا. أين ينبغي أن يقع الحدّ.
خطّ صور مُحسَّن في كل مكان إلا في الموضع الذي يهمّ. صورة LCP مُحمّلة بالتأجيل، أو بلا أبعاد، أو مُقدَّمة عبر تحويل يضيف 300 مللي ثانية قبل أول بايت. ما تكلفة ذلك، وكيف يُصلَح.
مسار صار ديناميكيًا دون أن يقرّر ذلك أحد. استدعاء cookies() واحد في
مكوّن مشترك يُخرج كل مسار تحته من العرض الثابت، فتتوقّف شبكة التوزيع عن
الاستجابة. الصفحة لم تبطؤ بسبب الشيفرة التي فيها؛ بطؤت لأنها كفّت عن أن
تُخزَّن مؤقتًا.
أطراف ثالثة تُحمَّل كأنها مجانية. مدير وسوم يجرّ أربعة سكربتات أخرى، وأداة محادثة تُرطَّب قبل المحتوى، ولافتة موافقة تحجب العرض الذي كان يُفترض بها أن تشترطه فقط. هذه ليست شيفرة تطبيق، ولهذا السبب بالضبط لا يقيسها أحد.
خطوط تحجب النصّ الذي وُجدت من أجله. خط مُستضاف ذاتيًا بلا font-display،
أو ثلاثة أوزان مُحمَّلة حيث يُستعمل واحد أعلى الصفحة.
ما الذي لا نفعله
لا نُحسّن لمواجهة درجة مختبريّة اخترتموها سلفًا. إن كانت بيانات ميدانكم تقول إن الزوّار بخير وتقول أداة إن الدرجة 62، فالجواب الصادق أن الأداة تقيس جهازًا وشبكةً لا يملكهما جمهوركم - وسنقول ذلك بدل بيع دورة عمل.
ولا نلتفّ على سبب لا نستطيع إصلاحه بتسريع شيء آخر تعويضًا. إن كانت مشكلة LCP طرفًا ثالثًا يحتاجه فريق التسويق لديكم، فذلك مذكور في النتائج، والقرار في هذه المفاضلة يعود إليكم بدل أن يُدفن في ملخّص.
ما الذي تحصلون عليه
وثيقة نتائج مرتّبة بالأولوية، كل مشكلة فيها متتبَّعة إلى سببها ومُقدَّرة بالجهد؛ والإصلاحات المنفَّذة على هيئة pull requests قابلة للمراجعة؛ وإعداد ميزانية التكامل المستمر؛ ومقارنة قبل/بعد على بيانات الميدان بعد أن يمضي على التغيير في الإنتاج ما يكفي لتحريك نافذة الثمانية والعشرين يومًا.
وإن كانت المعمارية نفسها هي السقف - وفي بعض التطبيقات هي كذلك - فذلك أيضًا في النتائج، وبجانبه كلفة البديل. هذا حديثٌ موضعه بداية المشروع لا نهايته.
