في 2025، أصبحت Core Web Vitals أكثر ذكاءً وتفاعلية، لكنها ما زالت تكشف نفس المشاكل القديمة. اكتشف كيف تغيرت مقاييس الأداء، وما الذي بقي عالقاً في ذاكرة المتصفحات، ولماذا لا يزال المطورون يكافحون مع نفس الأخطاء منذ 2020.
في صباح يوم عادي من عام 2025، فتحت لوحة تحكم Google Search Console لأحد المشاريع الكبيرة الذي أعمل عليه، ووجدت أن مؤشر Largest Contentful Paint (LCP) انخفض فجأة من 1.8 ثانية إلى 3.2 ثانية. لم أغير أي شيء في الكود، ولم أضف أي مكتبة جديدة، ولم أرفع أي صورة ثقيلة. المشكلة؟ تحديث جديد في Chrome غيّر طريقة حساب وقت تحميل الصور الديناميكية. هذا هو العالم الذي نعيش فيه الآن: Core Web Vitals لم تعد مجرد أرقام ثابتة، بل أصبحت كائنات حية تتنفس وتتأثر بكل تحديث للمتصفح، وكل تغيير في سلوك المستخدم، وحتى بكل بايت إضافي في ملف CSS.
لكن هنا تكمن المفارقة: رغم كل التطورات التقنية، ما زالت نفس الأخطاء القديمة تهيمن على تقارير الأداء. المطورون ما زالوا يرسلون صوراً غير مضغوطة، وما زالوا يحمّلون JavaScript بحجم 5 ميجابايت في الصفحة الأولى، وما زالوا يستخدمون مكتبات كاملة لتنفيذ مهام بسيطة يمكن حلها بـ 10 أسطر من الكود. في هذا المقال، سنفكك ما تغير في Core Web Vitals منذ 2020، وما الذي بقي كما هو، ولماذا لا تزال هذه المقاييس تمثل التحدي الأكبر للمطورين في 2025.
في البداية، كان Largest Contentful Paint يقيس ببساطة وقت ظهور أكبر عنصر مرئي في الصفحة، سواء كان صورة أو نصاً أو فيديو. لكن في 2023، بدأت Google في اختبار مفهوم "Dynamic LCP"، حيث أصبح المتصفح يتتبع العنصر الذي يجذب انتباه المستخدم فعلاً، وليس مجرد العنصر الأكبر مساحة. مثلاً، إذا كان لديك صورة كبيرة في الهيدر، لكن المستخدم يتفاعل فوراً مع زر "اشترِ الآن" في المنتصف، فقد يصبح هذا الزر هو العنصر المعني بالقياس. هذا التغيير لم يكن مجرد تحديث بسيط، بل غيّر تماماً طريقة تفكيرنا في تصميم الصفحات.
لكن المشكلة الأكبر ظهرت عندما بدأ المتصفح في حساب LCP بناءً على سلوك المستخدم الفعلي، وليس مجرد توقيت ثابت. هذا يعني أن نفس الصفحة قد تسجل LCP مختلفاً تماماً بين مستخدم وآخر، بناءً على مكان تركيزه وسرعته في التمرير. في مشروع عملت عليه مؤخراً، وجدنا أن LCP يختلف بنسبة تصل إلى 40% بين المستخدمين الذين يدخلون الصفحة لأول مرة وبين الذين يعودون إليها. الحل؟ لم يعد كافياً تحسين الصور والنصوص فقط، بل أصبحنا مضطرين لتحليل سلوك المستخدم باستخدام أدوات مثل Chrome User Experience Report (CrUX) وجمع بيانات حقيقية من الميدان.
// مثال على كيفية تتبع Dynamic LCP باستخدام PerformanceObserver
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
// إرسال البيانات إلى Analytics مع معلومات إضافية عن سلوك المستخدم
if (lastEntry) {
const userInteraction = window.performance.getEntriesByType("navigation")[0].type;
const scrollPosition = window.scrollY;
const viewportHeight = window.innerHeight;
// هنا نضيف سياق المستخدم الفعلي إلى بيانات LCP
const lcpData = {
value: lastEntry.startTime,
element: lastEntry.element ? lastEntry.element.tagName : 'N/A',
interactionType: userInteraction,
scrollRatio: scrollPosition / viewportHeight,
isReturningVisitor: document.cookie.includes('returning_visitor=1')
};
// إرسال البيانات إلى السيرفر لتحليلها
navigator.sendBeacon('/analytics/lcp', JSON.stringify(lcpData));
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });Cumulative Layout Shift (CLS) كان دائماً المقياس الأكثر إزعاجاً للمطورين، لأنه يعتمد على سلوكيات يصعب السيطرة عليها بالكامل. في 2020، كان يكفي تجنب تحميل الإعلانات بشكل مفاجئ أو تحديد أبعاد الصور مسبقاً. لكن في 2025، أصبح CLS أكثر تعقيداً بكثير. الآن، المتصفحات تقيس ليس فقط التحولات المفاجئة، بل أيضاً التحولات "المرئية" التي تحدث بسبب يشن أو تأثيرات التمرير المعقدة. مثلاً، إذا كان لديك قائمة منسدلة تنزلق ببطء عند الضغط عليها، فقد يسجل المتصفح هذا كتحول في الـ Layout، حتى لو كان التأثير مقصوداً ومحسوباً بدقة
الشيء المحبط هنا هو أن نفس الكود الذي كان يعمل بشكل مثالي في 2022 قد يسجل الآن CLS سيئاً بسبب تحديث في خوارزمية المتصفح. في أحد المشاريع، كان لدينا تأثير انزلاقي جميل لعناصر القائمة، مكتوب باستخدام CSS Transform. فجأة، بعد تحديث Chrome، بدأنا نرى CLS بقيمة 0.3 في الصفحات التي كانت تسجل 0.0 قبل أسبوع. السبب؟ المتصفح بدأ في حساب التحولات التي تحدث خلال 500 مللي ثانية بعد تفاعل المستخدم، حتى لو كانت هذه التحولات جزءاً من تصميم متكامل. الحل؟ اضطررنا لإعادة كتابة التأثير باستخدام Web Animations API والتحكم في توقيت كل إطار بدقة، مع إضافة `will-change: transform` للعناصر المتحركة.
/* حل متقدم لتجنب CLS مع تأثيرات انزلاقية معقدة */
.dropdown {
/* تحديد الحجم مسبقاً لتجنب التحولات المفاجئة */
height: 0;
overflow: hidden;
transition: height 0.3s ease-out;
will-change: height; /* تحذير المتصفح مسبقاً */
contain: layout; /* تحسين الأداء ومنع تأثيرات جانبية */
}
.dropdown.open {
height: auto; /* لكن هذا يسبب CLS! */
/* الحل: استخدام max-height مع قيمة محددة مسبقاً */
max-height: 500px; /* قيمة يجب حسابها بدقة */
}
/* بديل أفضل باستخدام Web Animations API */
const dropdown = document.querySelector('.dropdown');
dropdown.animate([
{ height: '0px', opacity: 0 },
{ height: '200px', opacity: 1 }
], {
duration: 300,
fill: 'forwards',
easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
});في مارس 2024، أعلنت Google رسمياً عن استبدال First Input Delay (FID) بـ Interaction to Next Paint (INP). هذا التغيير لم يكن مجرد تعديل بسيط في المقياس، بل كان اعترافاً بأن FID كان مقياساً غير كافٍ منذ البداية. FID كان يقيس فقط التأخير في الاستجابة لأول تفاعل للمستخدم، لكنه لم يأخذ في الاعتبار حالة الصفحة بعد ذلك. مثلاً، إذا كان لديك زر يستجيب بسرعة، لكن الصفحة بأكملها تتجمد بعد الضغط عليه، فإن FID سيظل جيداً، لكن تجربة المستخدم ستكون مروعة.
INP، من ناحية أخرى، يقيس كل تفاعل للمستخدم على الصفحة، وليس فقط الأول. وهو لا يقيس فقط التأخير في الاستجابة، بل يقيس أيضاً الوقت الذي يستغرقه المتصفح لرسم الإطار التالي بعد التفاعل. هذا يعني أن INP يعكس بشكل أفضل ما يشعر به المستخدم فعلياً. في أحد المشاريع التي عملت عليها، كان لدينا تطبيق React مع الكثير من الـ Re-renders. FID كان يسجل 80 مللي ثانية (جيد جداً)، لكن INP كان يسجل 450 مللي ثانية (سيئ جداً). السبب؟ كل مرة يضغط المستخدم على زر، كان React يعيد رسم مكونات كاملة، مما يسبب تجمد الواجهة لعدة مئات من المللي ثانية. الحل؟ استخدام React.memo لتقليل الـ Re-renders، وتنفيذ Virtualization للقوائم الطويلة، وتحسين الـ Event Handlers باستخدام debounce وthrottle.
// تحسين INP باستخدام debounce و React.memo
import { useState, useCallback, memo } from 'react';
// استخدام memo لمنع إعادة الرسم غير الضرورية
const ExpensiveComp memo(({ data }) => {
// مكون مكلف في الرسم
return <div>{data.map(item => <Item key={item.id} item={item} />)}</div>;
});
function SearchBar({ onSearch }) {
const [query, setQuery] = useState('');
// استخدام debounce لتقليل عدد عمليات البحث
const debouncedSearch = useCallback(debounce((q) => {
onSearch(q);
}, 300), [onSearch]);
const handleChange = (e) => {
setQuery(e.target.value);
debouncedSearch(e.target.value);
};
return <input type="text" value={query} onChange={handleChange} />;
}
// تنفيذ debounce بشكل يدوي لتحسين الأداء
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}رغم كل التطورات في Core Web Vitals، ما زال المطورون يرتكبون نفس الأخطاء التي كانت تُرتكب في 2020. لماذا؟ لأن معظم هذه الأخطاء ليست تقنية بحتة، بل هي أخطاء في طريقة التفكير. مثلاً، لا يزال الكثير من المطورين يعتقدون أن تحسين الأداء هو شيء يُضاف في نهاية المشروع، وليس جزءاً أساسياً من عملية التطوير. في الواقع، الأداء يجب أن يكون جزءاً من كل قرار تصميم وكل سطر كود يُكتب منذ اليوم الأول.
أيضاً، لا يزال هناك اعتقاد خاطئ بأن استخدام أحدث التقنيات سيحل مشاكل الأداء تلقائياً. مثلاً، الكثير من المطورين يعتقدون أن استخدام Next.js أو Remix سيجعل تطبيقهم سريعاً تلقائياً. لكن الحقيقة هي أن هذه الأطر توفر أدوات لتحسين الأداء، لكنها لا تضمنه. في مشروع عملت عليه مؤخراً، كان لدينا تطبيق Next.js مع LCP يبلغ 4.5 ثانية. بعد التحقيق، اكتشفنا أن المطورين كانوا يستخدمون `getServerSideProps` لكل صفحة، حتى الصفحات التي لا تحتاج إلى بيانات ديناميكية. ببساطة، تحويل هذه الصفحات إلى Static Generation قلل LCP إلى 1.2 ثانية.
رغم كل التغييرات في Core Web Vitals، هناك أشياء أساسية لم تتغير ولن تتغير أبداً. أولاً، حجم الصفحة لا يزال مهماً. في 2025، لا يزال المتصفح يحتاج إلى تحميل كل بايت في الصفحة، وكل بايت إضافي يعني وقت تحميل أطول. ثانياً، وقت تنفيذ JavaScript لا يزال يؤثر بشكل كبير على الأداء. حتى مع كل التحسينات في محركات JavaScript، لا يزال الكود السيئ يؤدي إلى تجربة مستخدم سيئة. ثالثاً، الشبكة لا تزال عنق الزجاجة الأكبر. حتى مع انتشار 5G و6G، لا يزال هناك مستخدمون على شبكات بطيئة أو غير مستقرة.
أيضاً، بعض المبادئ الأساسية لم تتغير. مثلاً، تحميل الموارد الحرجة أولاً (Critical Rendering Path) لا يزال مهماً كما كان في 2010. استخدام التخزين المؤقت بشكل صحيح لا يزال يحسن الأداء بشكل كبير. وتقسيم الكود إلى أجزاء صغيرة (Code Splitting) لا يزال أفضل طريقة لتقليل وقت التحميل الأولي. في أحد المشاريع، قمنا بتطبيق هذه المبادئ فقط على موقع قديم، وحققنا تحسيناً بنسبة 60% في LCP و40% في INP، دون إضافة أي تقنيات جديدة.
في 2020، كانت أدوات قياس الأداء تعتمد بشكل أساسي على بيانات المختبر (Lab Data) التي تُجمع في بيئة مسيطر عليها. لكن في 2025، أصبحت البيانات الحقيقية من الميدان (Field Data) هي الملك. أدوات مثل Chrome User Experience Report (CrUX) وWeb Vitals Library أصبحت أكثر ذكاءً، وتوفر بيانات أكثر تفصيلاً عن سلوك المستخدم الفعلي. مثلاً، الآن يمكنك معرفة ليس فقط قيمة LCP، بل أيضاً ما إذا كان المستخدم قد تفاعل مع الصفحة قبل ظهور العنصر الأكبر أم لا.
أيضاً، ظهرت أدوات جديدة تستخدم الذكاء الاصطناعي لتحليل الأداء. مثلاً، أداة مثل SpeedCurve الآن تستخدم خوارزميات تعلم الآلة للتنبؤ بمشكلات الأداء قبل حدوثها، بناءً على نمط الكود وسلوك المستخدم. في أحد المشاريع، استخدمنا هذه الأداة للتنبؤ بمشكلة في INP قبل أن تظهر في بيانات CrUX، وقمنا بحلها قبل أن تؤثر على المستخدمين الفعليين. أيضاً، أصبحت أدوات مثل Lighthouse أكثر تكاملاً مع بيئات التطوير. الآن يمكنك تشغيل Lighthouse مباشرة من داخل VS Code والحصول على تقارير مفصلة أثناء كتابة الكود، وليس فقط بعد نشره.
# تشغيل Lighthouse من سطر الأوامر مع خيارات متقدمة
lighthouse https://example.com \
--output=json \
--output-path=./report.json \
--chrome-flags="--headless --no-sandbox" \
--only-categories=performance \
--throttling.cpuSlowdownMultiplier=4 \
--throttling.rttMs=150 \
--throttling.throughputKbps=1638.4 \
--emulated-form-factor=mobile \
--screenEmulation.width=360 \
--screenEmulation.height=640 \
--screenEmulation.deviceScaleFactor=2 \
--screenEmulation.mobile=trueإذا كان هناك شيء واحد تعلمته خلال السنوات الخمس الماضية في مجال تحسين أداء الويب، فهو هذا: لا تنتظر حتى تنتهي من بناء المشروع لتبدأ في التفكير في الأداء. الأداء ليس خطوة أخيرة في قائمة المهام، بل هو جزء أساسي من كل قرار تتخذه منذ اليوم الأول. عندما تكتب سطر كود، اسأل نفسك: هل هذا الكود ضروري؟ هل يمكن كتابته بطريقة أكثر كفاءة؟ هل سيؤثر على تجربة المستخدم؟ إذا أجبت بنعم على أي من هذه الأسئلة، فربما تحتاج إلى إعادة التفكير.
أيضاً، لا تعتمد فقط على أدوات القياس الآلية. اختبر تطبيقك بنفسك على أجهزة حقيقية، في ظروف شبكة سيئة، ومع مستخدمين حقيقيين. الأرقام التي تراها في Lighthouse أو WebPageTest قد لا تعكس ما يشعر به المستخدم الفعلي. في النهاية، الأداء ليس مجرد أرقام في تقرير، بل هو شعور المستخدم عندما يتفاعل مع تطبيقك. اجعل هذا الشعور سلساً وسريعاً، وستكون قد فزت بمعركة Core Web Vitals.
الأداء ليس ميزة، بل هو حق للمستخدم. إذا لم تقدم تجربة سريعة وسلسة، فأنت تخذل مستخدميك.
— مطور مجهول في Google I/O 2023