العودة إلى المدونة

مؤشرات أداء الويب الأساسية للمواقع المتحركة: اجتياز LCP وINP وCLS

مؤشرات أداء الويب الأساسية للمواقع المتحركة: اجتياز LCP وINP وCLS

6 أكتوبر 2026 — الأداء

تاريخ النشر

6 أكتوبر 2026

مدة القراءة

4 دقيقة للقراءة

التصنيف

الأداء

الوسوم

  • Core Web Vitals
  • Performance
  • Animation
  • WebGL
  • LCP
  • INP
  • CLS

(مقدمة)كيف تحتفظ بحركات التمرير وWebGL والفيديو في موقعك وتجتاز مع ذلك مؤشرات أداء الويب الأساسية (Core Web Vitals). الأسباب المحددة لبطء LCP وINP وCLS، وطرق إصلاحها.

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

في هذا الموقع واجهة رئيسية بالفيديو، وجدار مشاريع بتقنية WebGL، ونصوص WebGL، وحركات مرتبطة بالتمرير في كل مكان. وهذه هي القواعد التي بُني عليها.

المؤشرات الثلاثة باختصار

المؤشر ما يقيسه جيد
LCP — Largest Contentful Paint متى يظهر المحتوى الرئيسي ≤ 2.5 ث
INP — Interaction to Next Paint سرعة استجابة الصفحة للنقر واللمس ≤ 200 مللي ث
CLS — Cumulative Layout Shift مقدار قفز التخطيط ≤ 0.1

تقيّمها Google عند المئين الخامس والسبعين لمستخدمي Chrome الحقيقيين (البيانات الميدانية في PageSpeed Insights وSearch Console)، لا بتشغيل Lighthouse على حاسوبك. اختبر على هاتف Android متوسط الفئة مع اتصال مُبطّأ؛ فهذا أقرب إلى المئين الخامس والسبعين لديك من جهاز MacBook.

LCP: اعرض شيئًا حقيقيًا مبكرًا

لا تُخفِ الواجهة الرئيسية خلف حركة دخول

أكثر ما يقتل LCP في المواقع المتحركة واجهة رئيسية تبدأ بـ opacity: 0 ثم تظهر تدريجيًا بعد تحميل JavaScript. لا يستطيع المتصفح احتسابها مرسومة حتى تصبح مرئية، فينتظر LCP حزمة الشيفرة، ثم الحركة.

الحل: اعرض الحالة النهائية للواجهة في HTML وCSS. حرّك العناصر الثانوية عند دخولها، أو ابدأ الحركة من حالة مرئية أصلًا (تغيير طفيف في الحجم أو الموضع، لا من شفافية صفرية).

امنح عنصر LCP صورة غلاف وأولوية

إذا كانت الواجهة فيديو أو لوحة WebGL، فإن مرشّح LCP عادةً هو صورة الغلاف (poster). اجعلها صغيرة، ومحمّلة مسبقًا، وذات أولوية عالية:

html
<link rel="preload" as="image" href="/hero/poster.avif" fetchpriority="high" />
<img src="/hero/poster.avif" alt="" fetchpriority="high" width="1600" height="900" />

ابدأ الفيديو أو مشهد WebGL بعد رسم صورة الغلاف، ثم انتقل إليه بتلاشٍ متداخل.

حمّل المكتبات الثقيلة عند الحاجة

لا داعي لأن تكون Three.js وإضافات GSAP وLottie في الحزمة الأولى إن كانت تشغّل أقسامًا أسفل الصفحة. استوردها ديناميكيًا عندما يقترب القسم من نافذة العرض:

ts
const observer = new IntersectionObserver(async ([entry]) => {
  if (!entry.isIntersecting) return
  observer.disconnect()
  const { initScene } = await import('./scene')
  initScene(canvas)
}, { rootMargin: '400px 0px' })
observer.observe(section)

في Next.js، يؤدي next/dynamic مع ssr: false الوظيفة نفسها لمكونات كاملة.

قدّم وسائط بالحجم المناسب

مقطع فيديو متكرر بدقة 1440p على هاتف هدرٌ للبيانات ينافس صورة LCP. اختر المصادر حسب عرض الشاشة ونوع الاتصال، واجعل مقاطع الواجهة قصيرة (من 6 إلى 10 ثوانٍ، بلا مسار صوتي).

INP: أبقِ الخيط الرئيسي متفرغًا

يقيس INP التأخير بين التفاعل والإطار التالي. وتضر الحركة به عندما يكون JavaScript مشغولًا في كل إطار.

حرّك transform وopacity فقط

تتولاهما وحدة التركيب (compositor) ولا تستدعيان إعادة حساب التخطيط. أما تحريك top أو left أو width أو height أو margin فيفرض إعادة التخطيط في كل إطار:

css
/* Slow: layout every frame */
.card { transition: top 0.3s; }

/* Fast: compositor only */
.card { transition: transform 0.3s; }

لا تقرأ التخطيط داخل معالجات التمرير

استدعاء getBoundingClientRect() بعد تغيير في الأنماط يفرض حساب تخطيط متزامنًا. في معالجات التمرير، اقرأ مرة واحدة لكل إطار داخل requestAnimationFrame، ثم اكتب:

ts
let ticking = false
window.addEventListener('scroll', () => {
  if (ticking) return
  ticking = true
  requestAnimationFrame(() => {
    const top = section.getBoundingClientRect().top   // read
    progressBar.style.transform = `scaleX(${clamp(-top / range)})` // write
    ticking = false
  })
}, { passive: true })

والأفضل من ذلك أن تترك مكتبة مثل ScrollTrigger من GSAP تجمّع هذا نيابةً عنك، أو تستخدم حركات CSS المرتبطة بالتمرير (animation-timeline: scroll()) حيث تكون مدعومة.

أوقف العمل الذي لا يُرى

كل حلقة requestAnimationFrame في الصفحة تعمل على الخيط الرئيسي. أوقف اللوحات والفيديوهات والحلقات مؤقتًا عندما تخرج من نافذة العرض، وعندما يكون التبويب مخفيًا:

ts
const io = new IntersectionObserver(([e]) => (e.isIntersecting ? start() : stop()))
io.observe(canvas)
document.addEventListener('visibilitychange', () => (document.hidden ? stop() : start()))

ضع حدًّا لدقة اللوحة

لوحة WebGL بقيمة devicePixelRatio تساوي 3 على الهاتف ترسم تسعة أضعاف البكسلات مقارنة بـ DPR 1. ضع حدًّا لها:

ts
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

وفي التأثيرات التي تملأ الشاشة مثل الضباب أو الضوضاء، كثيرًا ما يتعذر تمييز الرسم بنصف الدقة ثم تكبيره.

قسّم المهام الطويلة

بناء مشهد، أو تحليل ملف JSON كبير، أو تجهيز عدد كبير من أشكال النصوص قد يحجب الخيط لمئات الأجزاء من الثانية. قسّم العمل وأفسح المجال بين الأجزاء (await scheduler.yield() حيث يتوفر، وإلا setTimeout(0)) حتى تُلبّى أي نقرة أثناء الإعداد بسرعة.

CLS: احجز مساحة لكل شيء

حدّد أبعاد الوسائط

كل صورة وفيديو وiframe ولوحة تحتاج إلى خاصيتي width/height أو إلى aspect-ratio في CSS، ليحجز المتصفح المساحة قبل التحميل.

حمّل الخطوط دون قفزات

خطوط العناوين جزء من شخصية الموقع المتحرك، وتبديل الخط متأخرًا يزيح كل سطر. حمّل مسبقًا الملف أو الملفين الأساسيين، واقتطع منهما الأحرف المستخدمة فقط، واستخدم font-display: swap مع خط احتياطي معدّل المقاييس (size-adjust وascent-override)، وهو ما يولّده next/font تلقائيًا.

الحركات لا تُحتسب، أما تغييرات التخطيط فتُحتسب

يتجاهل CLS الحركات المعتمدة على transform، لكنه لا يتجاهل تغيير الحجم أو الموضع عبر خصائص التخطيط. شريط يدفع المحتوى إلى الأسفل بعد التحميل يُحتسب؛ أما الشريط نفسه حين ينزلق بـ transform إلى مساحة محجوزة فلا يُحتسب.

احترم تفضيل تقليل الحركة

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

css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

وفي JavaScript، تحقّق من matchMedia('(prefers-reduced-motion: reduce)') قبل تشغيل الحلقات وتأثيرات المنظور (parallax) والفيديو التلقائي.

طريقة عمل تكشف التراجعات

  1. سجّل تتبّع Performance في أدوات المطور في Chrome مع تبطيء المعالج 4 أضعاف، أثناء تمرير الصفحة كاملة. ابحث عن المهام الطويلة وأشرطة التخطيط البنفسجية.
  2. فعّل Rendering ← Paint flashing وLayout Shift Regions لترى ما يُعاد رسمه وما يتحرك.
  3. راجع البيانات الميدانية في PageSpeed Insights أسبوعيًا بعد الإطلاق؛ فنتائج المختبر للاسترشاد، والبيانات الميدانية هي العلامة النهائية.

قائمة التحقق

  • محتوى الواجهة الرئيسية مرئي في HTML الأولي، لا يظهر تدريجيًا من الصفر
  • صورة LCP محمّلة مسبقًا مع fetchpriority="high"
  • المكتبات الثقيلة تُحمَّل عند الطلب
  • تحريك transform وopacity فقط
  • قراءات التخطيط مجمّعة داخل requestAnimationFrame
  • إيقاف الحلقات واللوحات والفيديوهات خارج الشاشة
  • حدّ لنسبة بكسلات اللوحة
  • أبعاد لكل الوسائط؛ وخطوط مع بدائل مطابقة المقاييس
  • احترام prefers-reduced-motion

ولجانب الحركة من الموضوع، راجع GSAP في React وNext.js.

النشرة البريدية

تصلك المقالات الجديدة على بريدك

رسالة قصيرة عند نشر كل جديد. بلا رسائل مزعجة، ويمكنك إلغاء الاشتراك في أي وقت.

GSAP في React وNext.js: ‏useGSAP والتنظيف وScrollTrigger
GSAP في React وNext.js: ‏useGSAP والتنظيف وScrollTrigger
المقال التالي