دانيلا (⁦Dayfing⁩)
العودة إلى المقالات
2,527 كلمة13 د

مؤشرات Core Web Vitals في 2026: كيف تصلح LCP وINP وCLS

ابدأ بالبيانات الميدانية لا بدرجة Lighthouse. ابحث في Search Console أو PageSpeed Insights عن المقياس الذي يفشل عند المئين الخامس والسبعين وعن نوع الأجهزة الذي يحدث فيه ذلك، ثم أضف نسخة الإسناد من web-vitals لكي تكشف الجلسات الحقيقية العنصر أو التفاعل أو الإزاحة المسؤولة. أصلح LCP عبر أكبر أجزائه، وINP عبر المهمة الطويلة التي تقف خلف التفاعلات البطيئة، وCLS بحجز مساحة لكل ما يتحرك. تعيد أدوات المختبر إنتاج المشكلة وتتحقق من الإصلاح، لكنها لا تقرر هل تجتاز التقييم أم لا.

المقاييس الثلاثة وعتباتها

تسرد نظرة web.dev العامة على Web Vitals ثلاثة مقاييس مستقرة ضمن Core Web Vitals:

  • Largest Contentful Paint (LCP) لقياس التحميل: جيد عند 2.5 ثانية أو أقل، وضعيف فوق 4 ثوانٍ.
  • Interaction to Next Paint (INP) لقياس الاستجابة: جيد عند 200 ملي ثانية أو أقل، وضعيف فوق 500 ملي ثانية.
  • Cumulative Layout Shift (CLS) لقياس الثبات البصري: جيد عند 0.1 أو أقل، وضعيف فوق 0.25.

تُصنف القيم الواقعة بين الحدين بأنها «تحتاج إلى تحسين». تنطبق العتبات على المئين الخامس والسبعين من تحميلات الصفحة، مع فصل الهاتف المحمول عن سطح المكتب، ولا تجتاز الصفحة التقييم إلا إذا كانت المقاييس الثلاثة جيدة عند هذا المئين. قد يخفي وسيط مقبول ربع الزيارات القادمة من هواتف بطيئة، والتقييم موجود أصلاً لكشفها.

حل INP محل First Input Delay في عام 2024. وهو يراقب النقرات واللمسات وضغطات المفاتيح طوال عمر الصفحة، ويقيس كل تفاعل حتى رسم الإطار التالي. لا يُحتسب التمرير بالمؤشر ولا التكبير ولا التمرير، ويُستبعد التفاعل الأبطأ مقابل كل 50 تفاعلاً، فلا تحدد قيمة شاذة واحدة النتيجة.

يذكر ملف README الخاص بـ web-vitals أن onLCP() وonINP() تعملان في Chromium وFirefox وSafari، بينما تعمل onCLS() في Chromium فقط. وتُظهر بيانات التوافق في MDN أن Safari أضاف واجهتي LCP وEvent Timing في الإصدار 26.2. أما مجموعة البيانات الميدانية لدى Google فما زالت تُجمع من مستخدمي Chrome وحدهم.

البيانات الميدانية مقابل بيانات المختبر

تأتي البيانات الميدانية من زيارات حقيقية. يجمع Chrome UX Report (CrUX) بيانات مستخدمي Chrome المؤهلين في نافذة متحركة مدتها 28 يوماً، على مستوى الأصل وعنوان URL، للصفحات التي تملك حركة عامة كافية. يعرض PageSpeed Insights هذه البيانات في أعلى التقرير، ويعود إلى بيانات الأصل عندما تكون بيانات العنوان قليلة. ويعتمد تقرير Core Web Vitals في Search Console على المصدر نفسه، فيجمع العناوين المتشابهة في مجموعات، ويمنح كل مجموعة حالة أسوأ مقاييسها، ويتابع الهاتف المحمول وسطح المكتب كلاً على حدة. لذلك فإن إصلاحاً يُنشر اليوم يحرك هذه الأرقام تدريجياً على مدى أربعة أسابيع.

تأتي بيانات المختبر من تحميل واحد مضبوط. يحمّل Lighthouse الصفحة على جهاز وشبكة محاكَيين. أما لوحة Performance في DevTools فتعرض قيم LCP وCLS وINP المحلية مباشرة، وتسجل التفاعلات مع مراحلها وإزاحات التخطيط مع درجاتها، ويمكنها جلب بيانات CrUX مع اقتراح لإبطاء المعالج والشبكة يقارب ظروف مستخدميك.

يختلف النوعان لأسباب متوقعة، يشرحها web.dev في مقالة لماذا تختلف بيانات المختبر عن البيانات الميدانية:

  • LCP: يبدأ اختبار المختبر عادة بذاكرة تخزين مؤقت فارغة وحجم نافذة واحد ومن دون تخصيص. قد تكون الموارد مخزنة لدى المستخدمين الحقيقيين، وقد يختلف عنصر LCP أو تظهر نسخة مختلفة من اختبار A/B، كما تُحتسب الاستعادة من bfcache في الميدان.
  • INP: لا يتضمن تشغيل Lighthouse في وضع التنقل أي تفاعل، لذلك يعرض Total Blocking Time بدلاً منه. يساعد TBT في تشخيص الحظر أثناء التحميل، لكنه لا يرى نقرة بطيئة لاحقاً في الجلسة.
  • CLS: يرى المختبر الإزاحات أثناء التحميل. أما CLS الميداني فيغطي عمر الصفحة كله، بما في ذلك المحتوى المؤجل بلا أبعاد والإعلانات المتأخرة.

حدد ما هو معطل من البيانات الميدانية، ثم أعد إنتاجه في المختبر مع إبطاء واقعي. تعيد CrUX API المئين الخامس والسبعين مباشرة وتُحدَّث يومياً:

curl -s --request POST \
  "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$CRUX_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "origin": "https://example.com",
    "formFactor": "PHONE",
    "metrics": [
      "largest_contentful_paint",
      "interaction_to_next_paint",
      "cumulative_layout_shift",
      "largest_contentful_paint_image_time_to_first_byte",
      "largest_contentful_paint_image_resource_load_delay",
      "largest_contentful_paint_image_resource_load_duration",
      "largest_contentful_paint_image_element_render_delay"
    ]
  }'

المقاييس الأربعة الأخيرة هي أجزاء LCP للتحميلات التي يكون فيها عنصر LCP صورة.

اجمع بيانات المستخدمين الحقيقيين بنسخة الإسناد

يخبرك CrUX بأن مقياساً ما يفشل، ونادراً ما يخبرك بالسبب. تقيس مكتبة web-vitals المقاييس بالطريقة التي يقيسها بها Chrome، وتضيف نسخة الإسناد منها العنصر وتفصيل التوقيت وحالة تحميل المستند. الإصدار الرئيسي الحالي هو السادس. ويمكنه الإبلاغ عن soft navigations في تطبيقات الصفحة الواحدة على Chromium 151 وما بعده، لكن طريقة احتسابها في CrUX لم تُحسم بعد.

npm install web-vitals

تحتفظ هذه الوحدة بآخر سجل لكل نسخة من المقياس، وترسل دفعة عبر navigator.sendBeacon() عندما تصبح الصفحة مخفية، كما يوصي README:

import {onCLS, onINP, onLCP} from 'web-vitals/attribution';

const queue = new Map();

function toRecord(metric) {
  const {name, value, rating, id, attribution} = metric;
  const record = {name, value, rating, id, page: metric.navigationURL ?? location.href};

  if (name === 'LCP') {
    record.target = attribution.target;
    record.ttfb = attribution.timeToFirstByte;
    record.loadDelay = attribution.resourceLoadDelay;
    record.loadDuration = attribution.resourceLoadDuration;
    record.renderDelay = attribution.elementRenderDelay;
  } else if (name === 'INP') {
    record.target = attribution.interactionTarget;
    record.inputDelay = attribution.inputDelay;
    record.processing = attribution.processingDuration;
    record.presentation = attribution.presentationDelay;
    record.loadState = attribution.loadState;
    record.script = attribution.longestScript?.entry.sourceURL;
  } else if (name === 'CLS') {
    record.target = attribution.largestShiftTarget;
    record.loadState = attribution.loadState;
  }
  return record;
}

function flush() {
  if (queue.size === 0) return;
  navigator.sendBeacon('/rum', JSON.stringify([...queue.values()]));
  queue.clear();
}

onLCP((metric) => queue.set(metric.id, toRecord(metric)));
onINP((metric) => queue.set(metric.id, toRecord(metric)));
onCLS((metric) => queue.set(metric.id, toRecord(metric)));

addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') flush();
});

قد يُبلَّغ عن CLS وINP أكثر من مرة، لذلك يحتفظ المجمِّع بآخر قيمة لكل id. ولا يظهر INP إذا لم يتفاعل المستخدم مطلقاً. اجمع البيانات حسب قالب الصفحة ونوع الجهاز، ثم حسب target، لتعثر على العناصر القليلة التي تهيمن على p75. لن تطابق أرقامك CrUX تماماً، فهي تشمل متصفحات أخرى، والمكتبة لا ترى محتوى إطارات iframe. استخدم أخذ العينات في المواقع كثيفة الزيارات، وأبقِ عدد القيم المميزة للتسميات منخفضاً، وطبّق قواعد الاحتفاظ المعتادة. ويصف دليل قابلية ملاحظة وكلاء الذكاء الاصطناعي الانضباط نفسه في التعامل مع بيانات القياس عن بعد.

LCP: اعثر على الجزء البطيء

يقسم دليل تحسين LCP هذا المقياس إلى أربعة أجزاء متتالية:

  1. Time to first byte (TTFB): من بدء التنقل حتى أول بايت من HTML.
  2. Resource load delay: من TTFB حتى بدء طلب مورد LCP.
  3. Resource load duration: تنزيل المورد نفسه.
  4. Element render delay: من جاهزية المورد حتى رسم العنصر.

إذا كان عنصر LCP نصاً فإن الجزأين الأوسطين يساويان صفراً. يقترح الدليل توزيعاً تقريبياً: نحو 40 في المئة لكل من TTFB والتنزيل، وأقل من 10 في المئة لكل تأخير. وعند تطبيقه على 2.5 ثانية يصبح نحو ثانية واحدة، و250 ملي ثانية، وثانية واحدة، و250 ملي ثانية. التأخير الكبير في بدء التحميل يعني اكتشافاً متأخراً للمورد، والتأخير الكبير في العرض يعني أن شيئاً ما منع الرسم. ضغط الصورة لا يصلح أياً منهما.

إصلاحات LCP التي تحرك الرقم

Time to first byte

يعد web.dev قيمة TTFB البالغة 0.8 ثانية أو أقل جيدة، وما يتجاوز 1.8 ثانية ضعيفاً. ويشمل هذا الزمن عمليات إعادة التوجيه، وبدء service worker، وDNS، والاتصال وTLS، ثم الطلب نفسه. أزل سلاسل إعادة التوجيه، وخزّن HTML مؤقتاً على حافة CDN للزيارات المجهولة، وقدّم الملفات ذات الأسماء المختومة ببصمة مع مدة تخزين طويلة وثابتة. يشرح دليل التخزين المؤقت للمحتوى الثابت مع Nginx وCloudflare الترويسات والقواعد. وإذا كان الخادم الأصلي خادماً افتراضياً صغيراً، فأبقِه في حده الأدنى وفق قائمة تحصين خادم Linux الافتراضي، واترك الحافة تستوعب الحركة.

curl -s -o /dev/null \
  -w 'dns %{time_namelookup}\nconnect %{time_connect}\ntls %{time_appconnect}\nttfb %{time_starttransfer}\n' \
  https://example.com/

curl -sI https://example.com/ | grep -iE '^(cache-control|age|cf-cache-status):'

Resource load delay

يجب أن يبدأ تحميل صورة LCP مع أوائل موارد الصفحة. ضعها في HTML الأولي بوصفها <img>، ولا تضف إليها loading="lazy" أبداً، وأضف fetchpriority="high" الذي تذكر MDN دعمه في Chromium وFirefox 132 فما فوق وSafari 17.2 فما فوق. المسبب المعتاد هو قسم hero يُعرض على جهة العميل: لا يمكن طلب الصورة قبل أن تُنزَّل الحزمة وتُنفَّذ وتجلب البيانات في أحيان كثيرة. اعرض قسم hero على الخادم أو مسبقاً وقت البناء. وإذا كانت صورة LCP تأتي من CSS فحمّلها مسبقاً.

<link rel="preload" as="image" href="/img/hero-bg.avif"
      type="image/avif" fetchpriority="high">

<picture>
  <source type="image/avif"
          srcset="/img/hero-800.avif 800w, /img/hero-1600.avif 1600w"
          sizes="(max-width: 800px) 100vw, 800px">
  <img src="/img/hero-800.jpg"
       srcset="/img/hero-800.jpg 800w, /img/hero-1600.jpg 1600w"
       sizes="(max-width: 800px) 100vw, 800px"
       width="800" height="450" alt="Product dashboard"
       fetchpriority="high">
</picture>

استخدم <link> لصورة CSS فقط، و<picture> للمحتوى العادي.

Resource load duration

أرسل بايتات أقل. قدّم AVIF أو WebP مع صيغة احتياطية، ودع srcset وsizes يختاران ملفاً يطابق المساحة الفعلية، وقدّم الصور من CDN بمدة تخزين تجعل الزيارات المتكررة تتجاوز التنزيل. أبقِ الطلبات عالية الأولوية قليلة، لأن كل واحد منها ينافس صورة LCP.

Element render delay

إذا وصلت الصورة مبكراً ورُسمت متأخرة، فابحث عن أوراق أنماط كبيرة حاجبة للعرض، وسكربتات متزامنة داخل <head>، وخطوط تستخدم font-display: block أو auto. السكربتات التي تخفي الصفحة حتى تنتهي، كما تفعل بعض أدوات التجارب، تضيف زمن تشغيلها إلى LCP. ضمّن CSS الحرج داخل الصفحة، وأجّل الباقي، واعرض قسم hero من دون JavaScript على جهة العميل.

INP: اعثر على التفاعل ومهمته الطويلة

للتفاعل ثلاث مراحل. input delay هو الانتظار قبل تشغيل المعالجات، وغالباً ما يكون خلف مهمة أخرى. وprocessing duration هو زمن المعالجات نفسها. أما presentation delay فيمتد من نهاية المعالجات حتى الإطار التالي، ويشمل الأنماط والتخطيط والرسم.

اقرأ المراحل معاً. إذا ارتفع input delay وكانت قيمة loadState هي dom-interactive أو dom-content-loaded، فهذا يعني أن المستخدم لمس الشاشة بينما كانت الصفحة تقلع، وغالباً أثناء الإماهة أو تشغيل سكربتات خارجية. ارتفاع processing يشير إلى المعالج نفسه. وارتفاع presentation يشير إلى DOM كبير أو تخطيط قسري. وفي Chromium يسمي الحقل longestScript المستمد من Long Animation Frames API السكربت الذي استغرق أطول وقت.

لإعادة إنتاج المشكلة، افتح لوحة Performance، وطبّق إبطاء المعالج المقترح، وكرر التفاعل على العنصر الوارد في target من بيانات RUM. كل مهمة تتجاوز 50 ملي ثانية تُعد مهمة طويلة، والتفاعل الذي ينتظر خلفها يرث ما تبقى من زمنها.

إصلاحات INP: عمل أقل لكل تفاعل

ارسم الاستجابة أولاً

نفّذ الحد الأدنى الذي يعطي استجابة مرئية، ودع المتصفح يرسم، ثم تابع. نادراً ما تحتاج التحليلات أو الحفظ التلقائي إلى الانتهاء قبل الإطار التالي. ويعرض دليل المهام الطويلة هذه الدالة المساعدة:

function yieldToMain() {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

filterButton.addEventListener('click', async () => {
  filterButton.setAttribute('aria-busy', 'true');
  await yieldToMain();

  const rows = filterRows(allRows, currentQuery());
  renderRows(rows);
  filterButton.removeAttribute('aria-busy');

  await yieldToMain();
  sendAnalytics('filter', rows.length);
});

async function processInChunks(items, handleItem, budgetMs = 40) {
  let lastYield = performance.now();
  for (const item of items) {
    handleItem(item);
    if (performance.now() - lastYield > budgetMs) {
      await yieldToMain();
      lastYield = performance.now();
    }
  }
}

تنازل عن الخيط عبر scheduler.yield حيث يتوفر

تستأنف scheduler.yield() تنفيذ الكود بوصفه متابعة ذات أولوية، قبل المهام الأخرى المنتظرة ذات الأولوية المماثلة. وتصنفها MDN بأنها محدودة التوفر: Chrome وEdge 129 فما فوق، وFirefox 142 فما فوق، ولا يدعمها Safari. لذلك فإن اكتشاف الميزة مع بديل يعتمد على setTimeout() أمر لازم. يقسّم البديل المهمة أيضاً، لكنه يفقد الأولوية. ولم يعد web.dev يوصي بـ isInputPending()، بل ينصح بالتنازل عن الخيط في كل الأحوال. تُبقي دالة التقطيع كل مهمة دون 40 ملي ثانية، فلا تنتظر لمسة تحدث وسط الحلقة سوى مهمة قصيرة واحدة.

الإماهة والجزر

تشغّل إماهة الصفحة كاملة شجرة المكونات كلها أثناء التحميل، بما فيها المحتوى الثابت. وتعاني التفاعلات في هذه الفترة من تأخيرات إدخال طويلة. اعرض المحتوى الثابت بوصفه HTML، وأمِه المكونات التفاعلية فقط، وأجّلها حتى يتفرغ المتصفح أو تظهر على الشاشة. وفي Astro تحدد التوجيهات موعد إماهة كل جزيرة:

---
import SearchBox from '../components/SearchBox.jsx';
import Comments from '../components/Comments.jsx';
import PriceChart from '../components/PriceChart.jsx';
---
<SearchBox client:idle={{ timeout: 2000 }} />
<Comments client:visible={{ rootMargin: "200px" }} />
<PriceChart client:media="(min-width: 960px)" />

المكونات التي لا تحمل توجيهاً لا ترسل أي JavaScript. وتسعى React Server Components والإماهة الجزئية في أطر أخرى إلى النتيجة نفسها، لكن ينبغي قياسها لا افتراضها. راجع السكربتات الخارجية كذلك: احذف الوسوم غير المستخدمة، وحمّل الباقي وقت الفراغ، وراقب عناوينها في longestScript.

Presentation delay

تزداد كلفة العرض مع حجم DOM. أبقِ DOM صغيراً، واستخدم content-visibility: auto للأقسام الطويلة خارج الشاشة، ولا تقرأ offsetHeight مباشرة بعد كتابة الأنماط لأن ذلك يفرض تخطيطاً متزامناً. وانقل التحليل أو الفرز الثقيل للبيانات إلى Web Worker.

CLS: احجز المساحة وثبّت الخطوط

تساوي درجة إزاحة التخطيط حاصل ضرب نسبة التأثير في نسبة المسافة. وتتجمع الإزاحات التي يفصل بينها أقل من ثانية واحدة في نافذة جلسة أقصاها 5 ثوانٍ، ويساوي CLS أكبر نافذة. وتُستبعد الإزاحات التي تقع خلال 500 ملي ثانية من لمسة أو ضغطة مفتاح. ويسرد دليل تحسين CLS الأسباب المعتادة:

  • وسائط بلا أبعاد: حدد width وheight أو aspect-ratio كي تُحجز المساحة.
  • الإعلانات والمحتوى المضمّن واللافتات: احجز المكان بـ min-height، ولا تُدرج محتوى متأخراً فوق ما يقرؤه المستخدم أبداً.
  • خطوط الويب: استخدم font-display: optional، أو swap مع خط احتياطي مضبوط عبر size-adjust وتجاوزات ascent وdescent وline-gap، وحمّل الخطوط الحرجة مسبقاً.
  • الحركات: حرّك transform وopacity، لا top أو left أو width أو height. التحويلات المركبة لا تُحتسب في CLS.
  • التنقل للخلف وللأمام: تعرض الاستعادة من bfcache صفحة محمّلة من دون إزاحات، فأبقِ صفحاتك مؤهلة لها.
.ad-slot {
  min-height: 250px;
}

@font-face {
  font-family: "Brand Sans Fallback";
  src: local("Arial");
  size-adjust: 104%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

body {
  font-family: "Brand Sans", "Brand Sans Fallback", sans-serif;
}

.toast {
  transform: translateY(100%);
  transition: transform 200ms ease-out;
}

.toast.is-visible {
  transform: translateY(0);
}

نسب التجاوز هنا قيم توضيحية. احسبها من مقاييس خطك الفعلي، ثم قارن الخطين في المتصفح.

بماذا تبدأ

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

الإشارة الميدانية السبب المرجح الإصلاح الأول التأكيد عبر
LCP، يهيمن TTFB HTML غير مخزن مؤقتاً، إعادة توجيه تخزين على الحافة، بلا سلاسل إعادة توجيه توقيتات curl
LCP، يهيمن تأخير التحميل اكتشاف متأخر، hero مؤجل، عرض على العميل <img> في HTML، fetchpriority مخطط الشبكة الشلالي
LCP، تهيمن مدة التحميل صورة كبيرة جداً، صيغة قديمة AVIF أو WebP، srcset، CDN lcpResourceEntry
LCP، يهيمن تأخير العرض CSS أو سكربتات أو خطوط حاجبة CSS حرج، سكربتات مؤجلة تتبع Performance
INP، تأخير إدخال أثناء التحميل الإماهة، وسوم خارجية الجزر، إماهة مؤجلة loadState
INP، معالجة طويلة معالج ثقيل الرسم أولاً، yield، worker longestScript
INP، عرض طويل DOM كبير، تخطيط قسري DOM أصغر، content-visibility جدول التفاعلات
CLS أثناء التحميل وسائط بلا أبعاد، تبديل الخط الأبعاد، خط احتياطي مضبوط largestShiftTarget
CLS بعد التحميل محتوى متأخر، حركات تخطيط أماكن محجوزة، transform تبويب إزاحات التخطيط

تحقق من الإصلاح وامنع التراجع

أكّد كل تغيير مرتين. في المختبر قارن تتبعات Performance قبل التغيير وبعده مع إبطاء مبني على البيانات الميدانية، وتأكد من أن الجزء المستهدف قد تقلص. وفي الميدان راقب p75 في نظام RUM الخاص بك للقالب المتأثر، وهو يستجيب خلال أيام، ثم دع CrUX يلحق به على مدى 28 يوماً. بعد ذلك استخدم «بدء التتبع» في Search Console لبدء التحقق الذي يستمر 28 يوماً.

احتفظ بميزانية Lighthouse في CI لالتقاط السكربتات الحاجبة الجديدة والصور بلا أبعاد والقفزات في Total Blocking Time. لكنه لا يرى التفاعلات الحقيقية، لذلك أضف أيضاً تنبيهاً على INP الميداني، وعلّم عمليات النشر في لوحة RUM.

قائمة التحقق

  • راجع البيانات الميدانية في Search Console وPageSpeed Insights للهاتف المحمول وسطح المكتب.
  • سجّل المقياس الذي يفشل عند p75 في مجموعات العناوين الأعلى زيارة.
  • أضف نسخة الإسناد من web-vitals وأرسل beacon عند visibilitychange.
  • اجمع بيانات RUM حسب القالب ونوع الجهاز وtarget.
  • حدد الجزء المهيمن من LCP قبل اختيار الإصلاح.
  • ضع قسم hero في HTML مع fetchpriority="high"، ومن دون تحميل مؤجل، ومع srcset صحيح.
  • خزّن HTML على الحافة وأبقِ TTFB عند 0.8 ثانية أو أقل.
  • قسّم المعالجات الطويلة باستخدام scheduler.yield() مع بديل setTimeout().
  • أمِه الجزر التفاعلية فقط وراجع السكربتات الخارجية.
  • احجز مساحة للوسائط والإعلانات واللافتات، واضبط الخطوط الاحتياطية.
  • تحقق من كل إصلاح في تتبع مع إبطاء، ثم في RUM، ثم في CrUX.

مقالات أخرى