المراقبة الحقيقية للمستخدمين (Real User Monitoring، اختصارًا RUM) هي أسلوب لقياس أداء الويب من متصفحات المستخدمين الفعليين، بدلًا من محاكاة زيارة واحدة في مختبر افتراضي. بعبارة أوضح: RUM يجمع قيم LCP وINP وCLS وTTFB من كل جلسة حقيقية، ثم يُجمِّعها في شرائح مئوية (خصوصًا p75) تعكس ما يعيشه مستخدموك على شبكاتهم وأجهزتهم. صراحةً، بعد سنوات من تدقيق مواقع بميزانيات صغيرة وكبيرة، تبيّن لي أن الاعتماد على Lighthouse وحده كذبة مريحة؛ فبيانات الحقل هي ما يُحاكم به Google موقعك، وهي وحدها ما يُحرّك ترتيبك.
RUM يقيس Core Web Vitals من متصفحات حقيقية، بينما الاختبار الاصطناعي (Lighthouse) يُنفّذ جلسة واحدة في بيئة معملية ثابتة.
مكتبة web-vitals الرسمية (الإصدار 5.x في 2026) تُغطي جميع مقاييس CWV، وتوفّر بيانات تصحيح غنية عبر onLCP وonINP وonCLS.
Google يحكم على موقعك بالشريحة المئوية 75 (p75) لكل مقياس، مقسّمة بين سطح المكتب والجوّال في تقرير CrUX.
يجب إرسال القياسات عند حدث visibilitychange باستخدام navigator.sendBeacon لتفادي فقدان البيانات عند إغلاق التبويب.
CrUX يُقدّم بيانات مُجمّعة لعامة زوّار الموقع، لكن RUM الخاص يمنحك تقسيمًا حسب المسار، الجهاز، البلد، وحتى المكوّن الذي سبّب الانزياح.
الحد الأدنى لعينة موثوقة هو نحو 1000 عيّنة يوميًا لكل مسار (URL) قبل اتخاذ قرارات معمارية بناءً على الأرقام.
ما هو RUM بالضبط؟
RUM يعني ببساطة: تشغيل سكربت صغير في متصفح كل زائر، يستمع لأحداث الأداء عبر PerformanceObserver، يجمع القيم المتعلقة بـ Core Web Vitals، ثم يرسلها إلى خادم تحليلات أو نقطة نهاية خاصة بك. لا يُقاس أداء موقعك على جهاز مطوّر يحمل MacBook Pro M4 على اتصال ألياف بصرية؛ بل يُقاس على هاتف Android متوسط الفئة في بلد نائم فيه اتصال 4G المتقلّب، وربما مع 12 امتدادًا مثبتًا و7 تبويبات مفتوحة. هذا هو الواقع الذي يهتم به Google، لأنه واقع مستخدميك.
الفكرة نفسها ليست جديدة، فأدوات مثل New Relic Browser وDatadog RUM تجمع بيانات متصفحية منذ أكثر من عقد. الجديد في 2026 أن Google أصبح يفرض عتبات دقيقة على CWV، ويستخدم بيانات Chrome User Experience Report (المشتقة أساسًا من مستخدمي Chrome الذين وافقوا على مشاركة البيانات) عاملَ ترتيب فعلي في نتائج البحث. أي أنك لا تُراقب لأجل الفضول التقني، بل لأن كل ثانية تتأخر فيها LCP تعني ترتيبًا أدنى وإيرادات مفقودة.
يجب أن يستقي RUM قياسه من ثلاث حزم رئيسية من واجهات المتصفح: PerformanceObserver (لجمع LCP وCLS وLoAF)، Event Timing API (لـ INP)، وNavigation Timing (لـ TTFB). المكتبة الرسمية web-vitals تُغطّي كلها بسطر واحد لكل مقياس، وهذا ما سنبدأ به.
ما الفرق بين RUM والاختبار الاصطناعي؟
الاختبار الاصطناعي (Synthetic Monitoring) هو ما تفعله Lighthouse وPageSpeed Insights وإطار WebPageTest: جهاز افتراضي ثابت، شبكة محاكاة، ملفّ تعريف واحد، وجولة واحدة (أو محدودة). يمنحك ذلك نتائج قابلة للتكرار مفيدة لاختبار الانحدار في CI، لكنه لا يعكس تنوّع مستخدميك الحقيقيين. RUM بالمقابل مبني على التنوّع: آلاف الأجهزة، عشرات الشبكات، مئات المسارات.
البُعد
الاختبار الاصطناعي (Lighthouse)
RUM (بيانات الحقل)
حجم العيّنة
جلسة واحدة أو عدة جلسات مُنسَّقة
آلاف/ملايين الجلسات الفعلية
الجهاز والشبكة
محاكاة ثابتة (Moto G4 + 3G مثلًا)
الأجهزة والشبكات الحقيقية للزوّار
يعكس ترتيب Google؟
لا مباشرةً
نعم (عبر CrUX)
قياس INP دقيق
محدود؛ لا يوجد تفاعل بشري
ممتاز؛ يُقاس تفاعل حقيقي
الأنسب لـ
اختبار الانحدار في CI/CD قبل النشر
القرارات الاستراتيجية وأولويات التحسين
تكلفة الإعداد
منخفضة (أداة مجانية)
متوسطة (سكربت + تخزين + لوحة)
عمليًا، تحتاج الاثنين معًا. الاصطناعي يمنعك من نشر انحدار (regression) في build جديد؛ RUM يخبرك بالحقيقة التي يعيشها مستخدمك. لا تختر واحدًا فقط. في مشاريعي الأخيرة، أُشغِّل Lighthouse CI على كل PR كبوّابة، وأترك web-vitals يتدفّق ببيانات الإنتاج إلى BigQuery للتحليل الأسبوعي. إن كنت جديدًا على شرح TTFB في السياق نفسه، لدينا شرح مفصّل في مقالنا حول تحسين TTFB وتقليل زمن الاستجابة الأولى.
كيف تُنفّذ web-vitals في موقعك؟
مكتبة web-vitals من Google Chrome هي المرجع الفعلي، ونسختها الحالية في 2026 هي 5.x. حجمها أقل من 3KB بعد الضغط بـ gzip، ولا تعتمد على أي مكتبة خارجية. الاستخدام الأساسي بسيط: تُوَرِّد الدوال، تُسجِّل مُستمعًا لكل مقياس، ثم تُرسل النتيجة.
// npm install web-vitals@^5.0.0
// أو: <script type="module"> مع esm.sh/web-vitals
import { onLCP, onINP, onCLS, onTTFB, onFCP } from 'web-vitals';
// معالج موحّد لكل مقياس
function sendMetric(metric) {
const body = JSON.stringify({
name: metric.name, // 'LCP' | 'INP' | 'CLS' | 'TTFB' | 'FCP'
value: metric.value, // القيمة بالميلي ثانية (أو نسبة لـ CLS)
rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
delta: metric.delta, // الفرق منذ آخر تقرير
id: metric.id, // معرّف فريد للجلسة/المقياس
navigationType: metric.navigationType,
url: location.pathname,
deviceMemory: navigator.deviceMemory || null,
effectiveType: navigator.connection?.effectiveType || null,
});
// sendBeacon يعمل حتى أثناء إغلاق الصفحة
const url = '/api/rum';
(navigator.sendBeacon && navigator.sendBeacon(url, body))
|| fetch(url, { body, method: 'POST', keepalive: true });
}
// سجِّل جميع المقاييس الأساسية
onLCP(sendMetric);
onINP(sendMetric);
onCLS(sendMetric);
onTTFB(sendMetric);
onFCP(sendMetric);
هذا الكود جاهز للإنتاج، لكن أضف إليه دائمًا سياق تصحيح، وهو ما يُفرّق مبتدئ RUM عن محترفه. web-vitals 4.x فصاعدًا يوفّر خاصية attribution تُخبرك بأي عنصر تسبّب بالمشكلة تحديدًا:
import { onLCP, onINP, onCLS } from 'web-vitals/attribution';
onLCP((metric) => {
// metric.attribution يشمل:
// - element: العنصر الذي كان LCP فعليًا
// - url: مصدر الصورة/المورد
// - timeToFirstByte, resourceLoadDelay,
// resourceLoadDuration, elementRenderDelay
console.log('LCP element:', metric.attribution.element);
console.log('Load delay:', metric.attribution.resourceLoadDelay);
sendMetric({ ...metric, attribution: metric.attribution });
});
onINP((metric) => {
// attribution.interactionTarget يشير للعنصر الذي نقر عليه المستخدم
// attribution.longestScript يشير لأطول مهمة JS مسؤولة عن التأخير
console.log('INP target:', metric.attribution.interactionTarget);
console.log('Longest script:', metric.attribution.longestScript);
sendMetric({ ...metric, attribution: metric.attribution });
});
مع بيانات attribution، ستعرف بعد ساعات من النشر أن 60% من مشاكل LCP في صفحتك الرئيسية سببها صورة hero غير مضغوطة، أو أن INP السيّئ في صفحة السلّة سببه معالج onChange ثقيل في مكوّن كمية المنتج. هذا مستوى تشخيص لا يمنحك إياه أي اختبار اصطناعي. إن كنت تُعالج INP تحديدًا، راجع دليل تحسين مقياس INP لتفاعل أسرع في 2026.
إرسال القياسات إلى الخادم بشكل موثوق
المشكلة الأكثر شيوعًا في تطبيقات RUM الهاوية هي فقدان القياسات. مقاييس CLS وINP نهائية فقط عند مغادرة الصفحة (لأنهما تراكميّان)، وإذا استخدمت fetch() عادي، فسيتم إلغاء الطلب لحظة أن يُغلق المستخدم التبويب. الحل الصحيح ثلاثي:
استمع لحدث visibilitychange عندما تُصبح الحالة hidden، لا لحدث unload (فهذا الأخير غير موثوق على الجوّال، خصوصًا Safari iOS).
استخدم navigator.sendBeacon()، فهو مُصمَّم لإرسال بيانات صغيرة أثناء إغلاق الصفحة دون تعطيله.
ادعم fetch(..., { keepalive: true }) كخطة بديلة للمتصفحات النادرة التي لا تدعم sendBeacon.
// نقطة نهاية بسيطة بـ Node.js/Express لاستقبال قياسات RUM
import express from 'express';
import { BigQuery } from '@google-cloud/bigquery';
const app = express();
const bq = new BigQuery();
const dataset = bq.dataset('rum');
const table = dataset.table('web_vitals');
// نستقبل نصًا خامًا (لأن sendBeacon يرسل Blob/String)
app.post('/api/rum', express.text({ type: '*/*', limit: '16kb' }), async (req, res) => {
try {
const payload = JSON.parse(req.body);
// تحقّق مبدئي: ارفض المقاييس ذات القيم غير المنطقية
if (typeof payload.value !== 'number' || payload.value < 0) {
return res.status(400).end();
}
await table.insert({
timestamp: new Date().toISOString(),
metric_name: payload.name,
metric_value: payload.value,
rating: payload.rating,
url_path: payload.url,
device_memory: payload.deviceMemory,
effective_type: payload.effectiveType,
user_agent: req.get('user-agent'),
country: req.get('cf-ipcountry') || null, // مثال Cloudflare
});
// لا تُرجع محتوى، 204 كافٍ ويحافظ على النطاق
res.status(204).end();
} catch (err) {
// لا تُفشل الاستجابة بسبب مشكلة تحليل واحدة؛ فقط سجّلها
console.error('RUM ingest error:', err);
res.status(204).end();
}
});
app.listen(3000);
CrUX مقابل RUM الخاص: أيّهما تحتاج؟
Chrome UX Report (CrUX) هو مصدر Google الرسمي لبيانات CWV، يجمع الإحصاءات من مستخدمي Chrome الذين وافقوا على مشاركة إحصاءات الاستخدام. يُحدَّث شهريًا (بيانات BigQuery)، ويوميًا (PageSpeed Insights API)، ويومي/28-يومي (History API). إن كنت تريد أن تعرف "ما الذي يراه Google؟"، فـ CrUX هو الإجابة الرسمية.
لكن CrUX له قيود جوهرية:
يشمل مستخدمي Chrome فقط (نحو 65% من حصّة السوق العالمية في 2026، لكنها أقل في بعض الأسواق).
يحتاج نطاقك لعدد كافٍ من الزيارات لتظهر بياناتك (عتبة "adequate sample")؛ المواقع الصغيرة قد لا تُدرَج.
يجمع البيانات على مستوى النطاق (Origin) أو المسار (URL)، ولا يمكنك تقسيمها حسب نوع الجهاز الدقيق، أو حسب المكوّن الذي سبّب المشكلة.
يتأخر: البيانات المُجمّعة عبر 28 يومًا الماضية، فأي تحسين نشرته اليوم يظهر أثره في CrUX بعد أسابيع.
RUM الخاص يحلّ كل ذلك: تتحكّم في التقسيم (بحسب المسار، الجهاز، البلد، سرعة الشبكة، إصدار التطبيق، حتى A/B test bucket)، وتحصل على البيانات في ثوانٍ، وتشمل جميع المتصفحات لا Chrome فقط. الاستراتيجية المثلى: راقب CrUX أسبوعيًا كمُدقّق للحقيقة، وشغّل RUM يوميًا لاتخاذ قرارات التحسين. الاختلاف الكبير بين الاثنين نفسه إشارة تشخيصية؛ فإذا كان CrUX يُظهر p75 أعلى من RUM الخاص، فربما مستخدمو Chrome لديك مختلفون (أو أن RUM لا يُشغَّل على كل الصفحات).
لماذا p75 تحديدًا، وكيف تُحلِّل النتائج؟
Google اختار الشريحة المئوية 75 (p75) عمدًا لسبب واحد: 75% من مستخدميك يجب أن يحصلوا على تجربة "جيدة" حتى يعتبر المسار "جيدًا". المتوسط (mean) خادع لأن قيمة قصوى واحدة تُشوّهه، والوسيط (p50) متساهل جدًا لأنه يتجاهل نصف المستخدمين. p75 يقول: "المستخدم الذي في الربع الأخير من التوزيع يجب أن تكون تجربته مقبولة".
عتبات CWV في 2026 (لم تتغيّر عن 2020، لكن أضيف INP رسميًا في مارس 2024، وفق إعلان web.dev الرسمي):
المقياس
جيد (p75)
يحتاج تحسينًا
ضعيف
LCP
≤ 2.5 ث
2.5–4.0 ث
> 4.0 ث
INP
≤ 200 مللي ث
200–500 مللي ث
> 500 مللي ث
CLS
≤ 0.1
0.1–0.25
> 0.25
TTFB (إرشادي)
≤ 800 مللي ث
800–1800 مللي ث
> 1800 مللي ث
عند تحليل بيانات RUM، لا تنظر إلى الرقم الإجمالي فقط. قسّم دائمًا حسب:
نوع الجهاز: p75 على الجوّال شبه دائمًا أسوأ من سطح المكتب بضعف. Google يُقيّم كل واحد على حدة.
المسار (URL): صفحة المنتج قد تكون سيّئة رغم أن الصفحة الرئيسية ممتازة؛ المتوسط سيخفي ذلك.
سرعة الاتصال: قسّم حسب navigator.connection.effectiveType (4g/3g/2g).
البلد: مستخدمو الشرق الأوسط والهند قد يعانون من TTFB مرتفع إن كان خادمك في أمريكا، ولا يمكنك رؤية ذلك دون تقسيم جغرافي.
أفضل أدوات RUM في 2026
إن لم يكن لديك موارد لبناء pipeline كامل، فهناك أدوات جاهزة. أرتّبها بحسب ما استعملته فعليًا في مشاريع الإنتاج:
1. SpeedCurve LUX
مصمّم خصيصًا لـ CWV، ولوحة تحكم بديهية. يدمج بيانات RUM مع تسجيلات جلسات وشرائح مئوية تفصيلية. الأنسب للفرق التي تريد "لوحة قيادة تعمل من اليوم الأول" دون كتابة SQL.
2. DebugBear
يجمع بين RUM والاختبار الاصطناعي في مكان واحد، مع تنبيهات على انحدارات CWV. يبرز في تحليل attribution ويربطه بلقطات الشاشة.
3. Datadog RUM أو New Relic Browser
خيار المؤسسات التي تستخدم هذه المنصات أصلًا للمراقبة الخلفية. تربط ببيانات APM لتُظهر: "طلب API هذا كان بطيئًا، لذلك LCP هنا كان 4.2 ث". قوّتها في الربط، لا في CWV بحدّ ذاته.
4. Google Analytics 4 + web-vitals يدويًا
مجاني بالكامل. أرسل قيم web-vitals كأحداث مخصّصة (gtag('event', 'LCP', { value: ... }))، ثم أنشئ استكشافات في GA4. يعيبه أن GA4 يعتمد أخذ عيّنات (sampling) عند حجوم كبيرة، لكنه أفضل من لا شيء.
5. البناء الذاتي (self-hosted)
ما أفضّله شخصيًا: web-vitals يرسل إلى Cloudflare Worker أو Vercel Function، تُدرِج البيانات في ClickHouse أو BigQuery، ثم Grafana أو Metabase للعرض. تكلفة تحت 50 دولارًا شهريًا لمواقع تتلقّى مليون طلب يوميًا، وسيطرة كاملة على البيانات.
أخطاء شائعة تُفسد قياساتك
رأيت هذه الأخطاء تتكرّر في كل تدقيق تقريبًا:
تشغيل web-vitals قبل انتهاء تحميل الصفحة
إن أدرجت السكربت في <head> مع async، قد يبدأ التنفيذ قبل أن يُسجّل المتصفح أحداث LCP الأولى. المكتبة تتعامل مع هذا داخليًا، لكن تأكّد من عدم استعمال defer بشكل يؤخّرها لما بعد أول تفاعل.
عدم تجميع تقارير INP خلال SPA
في تطبيقات الصفحة الواحدة، تنقّل الطريق (route change) لا يُطلق visibilitychange. استخدم onINP مع خيار reportAllChanges: true لإرسال أعلى قيمة INP عند كل تغيير طريق، ثم أعِد ضبطها.
إهمال حالات الويب البطيء
قسمة الحسابات الإحصائية على "كل المستخدمين" تخفي الأسوأ. أنشئ لوحة منفصلة تُظهر p75 لمستخدمي 3G فقط. غالبًا تجد أن 5% من زوّارك يعيشون كارثة أداء، ولا أحد يعلم.
خلط RUM مع البوتات
Googlebot، برامج المراقبة الاصطناعية، أدوات المسح الأمني، كلها تُشوّه بياناتك. أضف مرشّحًا في نقطة الاستيعاب: تجاهل أي User-Agent يحتوي "bot" أو "crawler" أو "spider" أو "Headless".
الأسئلة الشائعة
هل RUM يبطئ موقعي؟
مكتبة web-vitals أصغر من 3KB بعد الضغط، وتعمل بشكل غير متزامن عبر PerformanceObserver. الأثر على LCP أقل من 5 ملي ثانية عمليًا. أضف نقطة استيعاب سريعة (Cloudflare Worker أو Edge Function) وستكون التكلفة الكلية غير محسوسة على المستخدم.
كم عيّنة أحتاج قبل أن أثق ببيانات RUM؟
القاعدة العملية: 1000 قياس يوميًا لكل مسار للحصول على p75 مستقر. أقل من ذلك، فترة الثقة (confidence interval) تكون واسعة جدًا لأن ترى فرقًا حقيقيًا بين نشرات. للمقاييس النادرة كـ INP في صفحات قليلة التفاعل، قد تحتاج أسبوعًا من التجميع.
هل يجب أن أشغّل RUM على 100% من الزوّار؟
نعم لمعظم المواقع. تكلفة الاستيعاب رخيصة، وأخذ العيّنات (sampling) يُفقدك القدرة على تشخيص المشاكل النادرة (كأخطاء INP التي تحدث لجهاز نادر فقط). لمواقع تتلقى >10 ملايين زيارة يوميًا، أخذ عيّنة 10% كافٍ ويوفّر التكلفة.
ما الفرق بين web-vitals و PerformanceObserver مباشرة؟
PerformanceObserver هو الواجهة الخام في المتصفح. web-vitals غلاف رقيق يُطبّق المنطق الرسمي لـ Google (متى يُعتبر LCP نهائيًا، كيف يُحسب CLS، كيف يُعزل INP). كتابة هذا يدويًا ممكن لكنها مصدر أخطاء دقيقة. استخدم المكتبة إلا إذا كان لديك سبب قوي جدًا.
هل يمكنني قياس Core Web Vitals لتطبيق داخل webview؟
نعم، طالما الـ webview يعتمد Chromium (WebView على Android أو WKWebView على iOS). لكن CrUX لا يجمع بيانات من webviews، لذا لن تظهر في PageSpeed Insights. RUM الخاص هو خيارك الوحيد لرؤية أداء تجربة webview.
ابدأ بسيطًا: أدرج web-vitals، أرسل إلى Google Analytics 4 عبر gtag، ثم راقب أسبوعًا. بعد أسبوعين ستملك ما لم يمنحك إياه Lighthouse أبدًا، صورة حقيقية عن كيف يعيش الناس موقعك. ومن تلك النقطة، كل قرار معماري تتخذه سيكون مبنيًا على أرقام لا على تخمين.
دليل عملي كامل لمقياس CLS في 2026: كيف يُحسب عبر نوافذ الجلسة، وأسباب انزياح التخطيط الفعلية، وحلول aspect-ratio وsize-adjust وView Transitions، مع كود قياس ميداني عبر PerformanceObserver وقائمة تدقيق جاهزة.
دليل عملي لتحسين مقياس INP في 2026: من قياسه بمكتبة web-vitals إلى تقسيم المهام بـ scheduler.yield واستخدام useTransition وWeb Workers لتفاعل تحت 200ms.
دليل عملي لسمة fetchpriority في HTML: كيف تستخدمها لتسريع LCP وتحسين Core Web Vitals في 2026، مع أمثلة كود ودراسات حالة من Google Flights و Etsy و Oodle.