في 2025، تغيرت قواعد اللعبة لأداء الويب: بعض مقاييس Core Web Vitals ماتت، وبعضها أصبح أكثر شراسة. اكتشف كيف تقيس الأداء اليوم، وما الذي يحدث خلف الكواليس في المتصفح عندما يتجمد موقعك، ولماذا لا يزال LCP وINP يقتلان تجربة المستخدم حتى في أفضل المواقع.
في صيف 2024، أعلنت جوجل عن تغييرات جذرية في كيفية تقييم أداء الويب. فجأة، اختفى أحد أهم مقاييس Core Web Vitals من لوحة تحكم Search Console، بينما ظهر مقياس جديد يهدد بتدمير ترتيب المواقع التي تعتمد على مكتبات جافاسكريبت الثقيلة. الحقيقة المؤلمة؟ معظم المطورين ما زالوا يقيسون الأداء بنفس الأدوات القديمة، ويستخدمون نفس الحلول التي كانت تعمل في 2022، بينما المتصفحات تطورت وأصبحت أكثر ذكاءً - وأشد قسوة.
الآن، إذا فتحت أداة Lighthouse في متصفح كروم، سترى ثلاثة مقاييس رئيسية: Largest Contentful Paint (LCP)، Interaction to Next Paint (INP)، وCumulative Layout Shift (CLS). لكن خلف هذه الأرقام البسيطة، هناك عالم معقد من العمليات التي تجري في الـ Event Loop، والـ Main Thread، والـ GPU. المشكلة الأكبر؟ معظم المطورين لا يفهمون حقاً ماذا تعني هذه الأرقام، وكيف تؤثر على تجربة المستخدم الحقيقية - وليس فقط على ترتيب البحث.
في الماضي، كنا نعتقد أن تحسين LCP يعني فقط ضغط الصور واستخدام CDN. لكن في 2025، أصبح الأمر أكثر تعقيداً. الآن، المتصفح لا ينظر فقط إلى وقت تحميل العنصر الأكبر على الصفحة، بل أيضاً إلى كيفية تأثير هذا العنصر على بقية الصفحة. مثلاً، إذا كان لديك صورة hero كبيرة في أعلى الصفحة، لكن تحميلها يسبب تجميد الـ Main Thread بسبب معالجة JavaScript ثقيلة، فإن LCP الخاص بك سيكون سيئاً حتى لو ظهرت الصورة بسرعة.
الحقيقة الصادمة؟ معظم المواقع تفشل في LCP ليس بسبب الصور، بل بسبب JavaScript. في دراسة حديثة أجرتها Cloudflare على 10,000 موقع، تبين أن 68% من المواقع التي لديها LCP سيء (>2.5 ثانية) كانت تعاني من مشاكل في تنفيذ JavaScript قبل ظهور الصورة الرئيسية. المشكلة تكمن في أن المتصفح لا يمكنه بدء رسم الصفحة (rendering) حتى ينتهي من تنفيذ الـ blocking scripts. وهذا يعني أن حتى لو كانت صورتك مخزنة في ذاكرة التخزين المؤقت (cache)، فإن LCP الخاص بك سيظل سيئاً إذا كان هناك script ثقيل يتم تنفيذه أولاً.
// مثال سيء: script ثقيل يمنع تحميل الصورة
>
// هذا الكود سيوقف الـ Main Thread لمدة 500ms
const start = performance.now();
while (performance.now() - start < 500) {}
// الصورة لن تبدأ في التحميل حتى ينتهي هذا الكود
const img = new Image();
img.src = 'hero-image.jpg';
</script>
<!-- الحل: تأجيل تنفيذ JavaScript غير الضروري -->
<script defer>
// هذا الكود سينفذ بعد تحميل الصفحة
document.addEventListener('DOMContentLoaded', () => {
// معالجة ثقيلة هنا
});
</script>
<!-- أو الأفضل: استخدام module scripts -->
<script type="module">
// الكود هنا سيتم تنفيذه بعد تحميل الصفحة
// ويمكن استخدام top-level await
const data = await fetch('/api/data');
</script>في 2025، تغيرت طريقة حساب LCP بشكل كبير. الآن، المتصفح لا ينظر فقط إلى وقت ظهور العنصر الأكبر، بل أيضاً إلى ما إذا كان هذا العنصر يسبب تجميد الصفحة. مثلاً، إذا كان لديك عنصر فيديو أو iframe كبير، فإن LCP سيتأثر بوقت تحميل هذا العنصر، حتى لو لم يكن مرئياً بالكامل. وهذا يعني أن تحسين LCP يتطلب الآن النظر إلى الصفحة ككل، وليس فقط إلى العنصر الأكبر.
أحد التغييرات الكبيرة الأخرى هو أن LCP الآن يأخذ في الاعتبار وقت تحميل الخطوط (fonts). إذا كان لديك خط مخصص يتم تحميله بعد ظهور المحتوى النصي، فإن هذا قد يسبب إعادة رسم الصفحة (reflow)، مما يؤثر سلباً على LCP. الحل؟ استخدام font-display: swap بحذر، أو الأفضل، تحميل الخطوط مسبقاً باستخدام preload.
<!-- تحميل الخط مسبقاً -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: optional; /* أو swap إذا كنت تريد تجنب FOIT */
}
</style>Interaction to Next Paint (INP) هو المقياس الجديد الذي حل محل First Input Delay (FID) في 2024. الفرق الرئيسي؟ FID كان يقيس فقط التأخير في الاستجابة لأول تفاعل، بينما INP يقيس زمن الاستجابة لجميع التفاعلات خلال جلسة المستخدم. وهذا يعني أن INP أكثر صرامة بكثير - موقع واحد يمكن أن يدمر درجتك إذا كان لديه تفاعل بطيء واحد فقط خلال الزيارة بأكملها.
المشكلة الأكبر مع INP هي أنه لا يقيس فقط التأخير في الاستجابة، بل أيضاً الوقت الذي يستغرقه المتصفح لرسم الإطار التالي بعد التفاعل. وهذا يعني أن حتى إذا كان JavaScript الخاص بك يستجيب بسرعة، فإن INP الخاص بك قد يكون سيئاً إذا كان هناك الكثير من العمل في الـ Main Thread بعد التفاعل. مثلاً، إذا كان لديك زر يقوم بتحميل بيانات جديدة ويحدث DOM بشكل مكثف، فإن INP سيتأثر حتى لو كان وقت الاستجابة الأولي سريعاً.
// مثال على تفاعل بطيء يؤثر على INP
button.addEventListener('click', async () => {
// هذا سيستغرق 200ms - سيء جداً لـ INP
const data = await fetch('/api/large-data');
// هذا سيستغرق 300ms إضافي بسبب تحديث DOM
const parsedData = await data.json();
document.getElementById('container').innerHTML =
parsedData.items.map(item => `<div>${item.name}</div>`).join('');
// الحل: تقسيم العمل واستخدام requestAnimationFrame
button.addEventListener('click', async () => {
const data = await fetch('/api/large-data');
const parsedData = await data.json();
// تقسيم تحديث DOM إلى أجزاء صغيرة
const c document.getElementById('container');
container.innerHTML = '';
parsedData.items.forEach((item, index) => {
requestAnimationFrame(() => {
container.innerHTML += `<div>${item.name}</div>`;
// إذا كان هناك الكثير من العناصر، استخدم virtual scrolling
});
});
});
});لفهم لماذا INP صعب التحسين، يجب أن نفهم كيف يعمل الـ Event Loop في المتصفح. عندما يقوم المستخدم بالتفاعل مع الصفحة (مثل النقر على زر)، يتم وضع حدث (event) في الـ Event Queue. ثم يقوم الـ Event Loop بمعالجة هذا الحدث عن طريق تنفيذ الـ callback المرتبط به. المشكلة تحدث عندما يكون الـ Main Thread مشغولاً بمعالجة شيء آخر (مثل تنفيذ JavaScript ثقيل أو حساب الأنماط)، مما يسبب تأخير في معالجة الحدث.
في 2025، أصبحت المتصفحات أكثر ذكاءً في التعامل مع هذه الحالات. مثلاً، كروم الآن يستخدم تقنية تسمى "Long Task Slicing" لتقسيم المهام الطويلة إلى أجزاء أصغر، مما يسمح للـ Event Loop بمعالجة الأحداث الأخرى بين هذه الأجزاء. لكن هذا لا يعني أن المطورين يمكنهم تجاهل تحسين الكود الخاص بهم. الحقيقة هي أن معظم مشاكل INP تأتي من الكود الذي يكتبه المطورون، وليس من قيود المتصفح.
// مثال على مهمة طويلة تقتل INP
function processLargeArray() {
const array = Array(1000000).fill().map((_, i) => i);
// هذه المهمة ستستغرق وقتاً طويلاً وستعطل الـ Event Loop
const result = array.reduce((acc, val) => acc + Math.sqrt(val), 0);
return result;
}
// الحل: تقسيم المهمة باستخدام setTimeout أو scheduler.postTask
function processLargeArrayOptimized() {
const array = Array(1000000).fill().map((_, i) => i);
const chunkSize = 10000;
let result = 0;
function processChunk(start) {
const end = Math.min(start + chunkSize, array.length);
for (let i = start; i < end; i++) {
result += Math.sqrt(array[i]);
}
if (end < array.length) {
// استخدام setTimeout للسماح للـ Event Loop بمعالجة الأحداث الأخرى
setTimeout(() => processChunk(end), 0);
} else {
console.log('Result:', result);
}
}
processChunk(0);
}Cumulative Layout Shift (CLS) هو المقياس الوحيد الذي بقي من Core Web Vitals الأصلي دون تغييرات كبيرة. لكن هذا لا يعني أنه أصبح أسهل في التحسين. في الواقع، مع زيادة تعقيد تطبيقات الويب الحديثة، أصبح CLS أكثر صعوبة في التحكم. المشكلة الرئيسية؟ معظم المطورين لا يفهمون حقاً كيف يحسب المتصفح CLS، مما يؤدي إلى حلول سطحية لا تعالج المشكلة الأساسية.
في 2025، أصبح المتصفح أكثر صرامة في حساب CLS. الآن، حتى التحولات الصغيرة في التخطيط (layout) يمكن أن تؤثر على درجتك، خاصة إذا حدثت بعد تفاعل المستخدم. مثلاً، إذا كان لديك زر يظهر محتوى إضافي عند النقر عليه، فإن أي تحرك في العناصر المحيطة سيحسب كجزء من CLS. وهذا يعني أن حتى التطبيقات المصممة جيداً يمكن أن تعاني من CLS سيء إذا لم يتم التعامل مع التحولات بطريقة صحيحة.
<!-- مثال على مشكلة CLS -->
<button id="toggle">عرض المزيد</button>
<div id="content" style="display: none;">
محتوى إضافي سيظهر عند النقر
</div>
>
document.getElementById('toggle').addEventListener('click', () => {
const c document.getElementById('content');
// هذا سيسبب CLS لأن المحتوى سيظهر فجأة
content.style.display = content.style.display === 'none' ? 'block' : 'none';
});
</script>
<!-- الحل: استخدام يشن أو حجز مساحة مسبقاً -->
<style>
#content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
#content.open {
max-height: 1000px; /* قيمة كبيرة بما يكفي */
}
</style>
<script>
document.getElementById('toggle').addEventListener('click', () => {
const content = document.getElementById('content');
content.classList.toggle('open');
// لا CLS لأن المساحة محجوزة مسبقاً
});
</script>لفهم CLS حقاً، يجب أن نعرف كيف يحسبه المتصفح. عندما يتم تحميل الصفحة، يقوم المتصفح برسم كل إطار (frame) على الشاشة. في كل إطار، يحسب المتصفح ما إذا كان هناك أي عنصر قد تغير موقعه أو حجمه مقارنة بالإطار السابق. إذا كان هناك تغيير، فإنه يحسب "impact fraction" (النسبة المئوية من الشاشة التي تأثرت بالتغيير) و"distance fraction" (مدى تحرك العنصر). ثم يضرب هذين الرقمين للحصول على "layout shift score" لهذا الإطار.
المشكلة الأكبر؟ المتصفح يحسب CLS لكل إطار، وليس فقط للأحداث الرئيسية. وهذا يعني أن حتى التحولات الصغيرة التي تحدث بسبب يشن أو تحميل الصور يمكن أن تؤثر على درجتك. في 2025، أصبح المتصفح أكثر ذكاءً في تجاهل بعض التحولات إذا كانت جزءاً من يشن سلس، لكنه لا يزال يحسب التحولات التي تحدث بسبب تفاعلات المستخدم أو تحميل الموارد.
في 2024، أعلنت جوجل عن إزالة First Input Delay (FID) من Core Web Vitals، واستبداله بـ Interaction to Next Paint (INP). السبب؟ FID كان مقياساً سطحياً يقيس فقط التأخير في الاستجابة لأول تفاعل، بينما INP يعطي صورة أكثر دقة عن تجربة المستخدم الحقيقية. لكن هذا لا يعني أن FID مات تماماً - لا يزال يستخدم كمقياس ثانوي في بعض الأدوات، لكنه لم يعد جزءاً من تقييم أداء الصفحة الرئيسي.
المفاجأة الأكبر كانت إزالة Time to First Byte (TTFB) من قائمة المقاييس الرئيسية. في الماضي، كان TTFB يعتبر مؤشراً هاماً لأداء السيرفر، لكن في 2025، أصبح واضحاً أن TTFB ليس له تأثير مباشر على تجربة المستخدم. لماذا؟ لأن معظم المواقع الحديثة تستخدم تقنيات مثل Server-Side Rendering (SSR) وStatic Site Generation (SSG)، مما يجعل وقت الاستجابة الأولي للسيرفر أقل أهمية من وقت تحميل المحتوى الفعلي. بالإضافة إلى ذلك، مع انتشار شبكات CDN وHTTP/3، أصبح TTFB أقل تنبؤاً بأداء الصفحة بشكل عام.
على الرغم من أن TTFB لم يعد جزءاً من Core Web Vitals، إلا أنه لا يزال مقياساً هاماً لفهم أداء السيرفر. مثلاً، إذا كان لديك TTFB بطيء (>500ms)، فهذا قد يشير إلى مشاكل في قاعدة البيانات أو عدم كفاءة الكود الخلفي. لكن الفرق الآن هو أن تحسين TTFB وحده لن يحسن بالضرورة تجربة المستخدم إذا كان بقية الصفحة بطيئة في التحميل.
في تجربتي، أفضل استخدام TTFB كمؤشر أولي لأداء السيرفر، بينما أركز على مقاييس Core Web Vitals الفعلية (LCP، INP، CLS) لتحسين تجربة المستخدم. مثلاً، في مشروع حديث عملت عليه، كان لدينا TTFB بطيء بسبب استعلامات قاعدة بيانات معقدة. لكن بعد تحسين الاستعلامات واستخدام التخزين المؤقت (caching)، انخفض TTFB بشكل كبير، مما أدى إلى تحسين ملحوظ في LCP وINP أيضاً.
في 2025، تغيرت أدوات قياس أداء الويب بشكل كبير. لم تعد أداة Lighthouse التقليدية كافية لفهم أداء موقعك الحقيقي. الآن، تحتاج إلى مجموعة من الأدوات التي تعطيك صورة كاملة عن كيفية تفاعل المستخدمين مع موقعك في العالم الحقيقي، وليس فقط في بيئة الاختبار المثالية.
الأداة الأولى التي يجب أن تستخدمها هي Chrome DevTools. لكن ليس بالطريقة التقليدية - تحتاج إلى استخدام ميزات مثل Performance Insights وWeb Vitals extension للحصول على بيانات دقيقة عن LCP وINP وCLS. على سبيل المثال، في علامة التبويب Performance، يمكنك تسجيل جلسة مستخدم حقيقية ورؤية بالضبط ما الذي يسبب تأخير في التفاعلات أو تحولات في التخطيط.
# تشغيل Lighthouse من سطر الأوامر مع إعدادات مخصصة
lighthouse https://example.com --output=json --output-path=./report.json \
--chrome-flags="--headless=new" --only-categories=performance \
--throttling.cpuSlowdownMultiplier=4 --throttling.rttMs=150
# تحليل بيانات حقيقية من المستخدمين باستخدام Chrome UX Report
# (تتطلب الوصول إلى Google BigQuery)
SELECT
origin,
form_factor.name AS device,
SUM(IF(bin.start < 2500, density, 0)) AS good_lcp,
SUM(IF(bin.start >= 2500 AND bin.start < 4000, density, 0)) AS needs_improvement_lcp,
SUM(IF(bin.start >= 4000, density, 0)) AS poor_lcp
FROM `chrome-ux-report.all.202501`
WHERE origin = 'https://example.com'
GROUP BY origin, deviceأحد أكبر الأخطاء التي يرتكبها المطورون هو الاعتماد فقط على بيانات Lighthouse لقياس أداء موقعهم. المشكلة؟ Lighthouse يعطي نتائج مثالية في بيئة اختبار خاضعة للرقابة، بينما تجربة المستخدم الحقيقية تختلف بشكل كبير. مثلاً، Lighthouse قد يعطي درجة ممتازة لموقعك في بيئة الاختبار، لكن في العالم الحقيقي، قد يعاني المستخدمون من بطء بسبب عوامل مثل: سرعة اتصال الإنترنت المتقلبة، الأجهزة القديمة، أو حتى تدخل الإضافات في المتصفح.
لهذا السبب، يجب أن تجمع بين البيانات المختبرية (مثل Lighthouse) والبيانات الحقيقية (مثل CrUX). مثلاً، في مشروع عملت عليه مؤخراً، كان Lighthouse يعطي درجة 95 للأداء، لكن بيانات CrUX أظهرت أن 40% من المستخدمين الحقيقيين يعانون من LCP سيء (>4 ثوانٍ). بعد التحقيق، اكتشفنا أن المشكلة كانت في تحميل الخطوط من CDN خارجي، والذي كان بطيئاً في بعض المناطق الجغرافية. بدون بيانات CrUX، ما كنا لنكتشف هذه المشكلة أبداً.
بعد عشر سنوات في تطوير الويب، تعلمت أن تحسين الأداء ليس مجرد مسألة تطبيق بعض الحيل السطحية. الأمر يتطلب فهماً عميقاً لكيفية عمل المتصفحات، وكيفية تفاعل المستخدمين مع المواقع، وما الذي يحدث خلف الكواليس في الـ Event Loop والـ Main Thread. إليك نصائحي الذهبية لتحسين Core Web Vitals في 2025:
في النهاية، تحسين Core Web Vitals ليس مجرد مسألة تحسين ترتيب البحث. الأمر يتعلق بتقديم تجربة مستخدم أفضل، وتقليل معدل الارتداد، وزيادة التحويلات. في 2025، أصبح الأداء جزءاً أساسياً من تجربة المستخدم، وليس مجرد ميزة إضافية. إذا كنت تريد أن ينجح موقعك، فعليك أن تأخذ الأداء على محمل الجد - ليس فقط لتحقيق درجة جيدة في Lighthouse، بل لتقديم تجربة سلسة وسريعة لجميع المستخدمين، بغض النظر عن جهازهم أو موقعهم الجغرافي.