Оптимизация на LCP (Largest Contentful Paint): Пълно ръководство за бързо зареждане

Пълно ръководство за оптимизация на LCP — метриката от Core Web Vitals за бързо зареждане. 9 стратегии с код: fetchpriority, HTTP 103 Early Hints, responsive изображения, SSR стрийминг и мониторинг с RUM данни.

Въведение: Защо LCP е метриката, която наистина има значение

Когато потребител отваря вашия уебсайт, първото впечатление се формира за броени секунди. И точно тук влиза в играта Largest Contentful Paint (LCP) — метриката от Core Web Vitals, която измерва колко бързо се зарежда основното съдържание на страницата. Ако hero изображението, заглавният текст или видеото не се появят за по-малко от 2,5 секунди — честно казано, потребителите вече натискат бутона „назад".

А Google понижава класирането ви.

Ако вече сте прочели нашето ръководство за оптимизация на INP (Interaction to Next Paint), знаете, че INP отговаря за отзивчивостта след зареждане. LCP обаче е метриката, която определя дали потребителят изобщо ще остане достатъчно дълго, за да взаимодейства с вашата страница. Двете метрики работят заедно — бързо зареждане (LCP) плюс плавно взаимодействие (INP) равно на отлично потребителско изживяване.

Статистиката е доста красноречива. Над 40% от уебсайтовете не покриват прага за добър LCP резултат според Chrome User Experience Report. Почти половината от интернет губи потребители и SEO позиции заради бавно зареждане — а повечето от проблемите са напълно решими. В това ръководство ще разгледаме абсолютно всичко, което трябва да знаете за LCP — от четирите подкомпонента на метриката до конкретни стратегии за оптимизация с реални примери с код.

Какво точно измерва LCP?

Largest Contentful Paint отчита времето от момента на навигация до визуализирането на най-големия видим елемент във viewport-а. Това може да бъде:

  • Изображение<img>, <image> в SVG, или CSS background-image
  • Видео — постерът на <video> елемент
  • Блоков текст — голям заглавен елемент (<h1>, <h2>) или параграф
  • Елемент с фоново изображение, заредено чрез url() функция

И ето нещо, което може да ви изненада: в 70–80% от случаите LCP елементът е изображение. Затова голяма част от оптимизациите, които ще разгледаме по-долу, са свързани именно с начина, по който зареждаме и показваме изображения.

Прагове за оценка на LCP

Google дефинира три категории:

  • Добър (Good): ≤ 2,5 секунди — потребителят усеща бързо зареждане
  • Нуждае се от подобрение (Needs Improvement): 2,5–4,0 секунди — забележимо забавяне
  • Лош (Poor): > 4,0 секунди — потребителят вероятно вече е натиснал бутона „назад"

Тези прагове се прилагат за 75-ия персентил на потребителските посещения. Тоест, дори средната ви стойност да е добра, достатъчно лоши резултати при бавни връзки или стари устройства могат да провалят цялостната оценка. Имайте го предвид.

Четирите подкомпонента на LCP: Разбиране на проблема

От 2025 г. Google препоръчва разбиването на LCP на четири подкомпонента (subparts), за да се идентифицира къде точно се губи времето. Данните от CrUX вече включват информация за тях, което прави диагностиката значително по-лесна.

Нека ги разгледаме един по един.

1. Time to First Byte (TTFB)

TTFB измерва времето от заявката за HTML документа до получаването на първия байт от отговора. Включва DNS резолюция, TCP/TLS хендшейк и времето за обработка на сървъра.

И числата са категорични: при поне половината от сайтовете с лош LCP, 75-ият персентил на TTFB е 2270 ms — което само по себе си почти гарантира, че LCP няма как да бъде под 2,5 секунди. Тоест, без добър TTFB, всичко останало е безсмислено.

2. Resource Load Delay (Забавяне на зареждането на ресурса)

Това е времето между получаването на първия байт от HTML-а и началото на изтегляне на LCP ресурса (обикновено изображение). Внимание — не е самото изтегляне, а времето за откриване на ресурса.

Средният сайт с лош LCP прекарва почти четири пъти повече време в чакане преди изтеглянето да започне, отколкото в самото изтегляне. Средно 1,3 секунди забавяне — повече от половината от бюджета от 2,5 секунди, изгубени в една-единствена подфаза. Честно казано, това ме изненада, когато за пръв път видях тези данни.

3. Resource Load Duration (Продължителност на зареждане)

Времето за изтегляне на самия LCP ресурс. Зависи основно от размера на файла и мрежовата честотна лента. Изненадващо, Google установява, че времето за изтегляне почти никога не е основното тесно място. Другите подкомпоненти са много по-сериозен проблем.

4. Element Render Delay (Забавяне на визуализацията)

Времето от момента, в който ресурсът е изтеглен, до момента, в който LCP елементът е реално видим на екрана. Причините могат да бъдат блокиращ CSS, зает main thread или A/B тестове, които скриват съдържание. В някои случаи Render Delay може да представлява до 96% от общото LCP — да, точно така, деветдесет и шест процента.

Как да измерите подкомпонентите

Ето как да получите детайлна разбивка на LCP подкомпонентите чрез JavaScript:

const LCP_SUB_PARTS = [
  'TTFB',
  'Load Delay',
  'Load Duration',
  'Render Delay',
];

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];

  const navEntry = performance.getEntriesByType('navigation')[0];
  const lcpResEntry = performance
    .getEntriesByType('resource')
    .find((e) => e.name === lastEntry.url);

  const ttfb = navEntry.responseStart;

  const lcpRequestStart = Math.max(
    ttfb,
    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
  );

  const lcpResponseEnd = Math.max(
    lcpRequestStart,
    lcpResEntry ? lcpResEntry.responseEnd : 0
  );

  const lcpRenderTime = Math.max(
    lcpResponseEnd,
    lastEntry.renderTime || lastEntry.loadTime
  );

  const subParts = {
    'TTFB': ttfb,
    'Load Delay': lcpRequestStart - ttfb,
    'Load Duration': lcpResponseEnd - lcpRequestStart,
    'Render Delay': lcpRenderTime - lcpResponseEnd,
  };

  console.table(subParts);

  // Изпращане към аналитичен сървис
  navigator.sendBeacon('/analytics/lcp', JSON.stringify(subParts));
});

observer.observe({ type: 'largest-contentful-paint', buffered: true });

Google препоръчва Resource Load Delay и Render Delay заедно да не надвишават 20% от общото LCP. Останалите 80% трябва да се разпределят приблизително поравно между TTFB и Load Duration. Добро правило за ориентир.

Стратегия 1: Оптимизация на TTFB

Тъй като TTFB е фундаментът на LCP, нека започнем оттук. Ако сървърът ви отговаря бавно, никаква оптимизация на фронтенда няма да компенсира — колкото и да полирате клиентската част.

Използване на CDN

Content Delivery Network (CDN) поставя копие на вашето съдържание на сървъри по целия свят. Когато потребител посети сайта ви, съдържанието се сервира от най-близкия сървър. Звучи просто, нали? Но ефектът е драматичен — особено за потребители, които са далеч от оригиналния ви сървър.

Кеширане на сървърната страна

Използвайте Redis, Memcached или вградения кеш на вашия фреймуърк, за да сервирате предварително генериран HTML:

// Express.js с Redis кеширане
import Redis from 'ioredis';

const redis = new Redis();

async function cachedHandler(req, res) {
  const cacheKey = `page:${req.url}`;

  // Проверка за кеширана версия
  const cached = await redis.get(cacheKey);
  if (cached) {
    res.set('X-Cache', 'HIT');
    return res.send(cached);
  }

  // Генериране на HTML
  const html = await renderPage(req.url);

  // Кеширане за 5 минути
  await redis.set(cacheKey, html, 'EX', 300);

  res.set('X-Cache', 'MISS');
  res.send(html);
}

Правилни Cache-Control хедъри

Настройте HTTP кеширане, за да позволите на CDN-а и браузъра да кешират ресурсите правилно:

# За статични ресурси (изображения, CSS, JS)
Cache-Control: public, max-age=31536000, immutable

# За HTML страници
Cache-Control: public, max-age=300, stale-while-revalidate=3600

Директивата stale-while-revalidate е особено полезна — позволява на CDN-а да сервира кеширано съдържание веднага, докато обновява версията във фонов режим. Потребителят получава моментален отговор, а съдържанието остава актуално. Печелите и от двете страни.

Стратегия 2: HTTP 103 Early Hints

Едно от най-въздействащите нововъведения за LCP оптимизация е HTTP 103 Early Hints. Идеята е гениално проста: докато сървърът подготвя окончателния отговор (200 OK), той изпраща предварителен 103 отговор с подсказки за ресурси, които браузърът може да започне да зарежда веднага.

Реални резултати от внедряване

Данните от реални внедрявания са наистина впечатляващи:

  • Akamai (2025): Клиент от хотелиерската индустрия достигна 82% от потребителите с „добър" LCP — 30% подобрение средно
  • Cloudflare + Shopify: LCP по-бърз с над 500 ms на 50-ия персентил по време на Black Friday
  • Независими тестове: LCP елемент се появява 35% по-рано; с включване на CSS — от 3,2 до 2,0 секунди

Тези числа не са от лабораторни условия, а от реални сайтове с реален трафик.

Как да конфигурирате Early Hints

Ако използвате Nginx (версия 1.27.3+), конфигурацията е сравнително проста:

# nginx.conf
server {
    listen 443 ssl;

    location / {
        # Изпращане на 103 Early Hints преди основния отговор
        add_header Link "; rel=preload; as=style" early;
        add_header Link "; rel=preload; as=image" early;
        add_header Link "; rel=preconnect" early;

        proxy_pass http://backend;
    }
}

За Cloudflare можете да активирате Early Hints от таблото за управление, а за Akamai — чрез Property Manager. Важното е да бъдете селективни — изпращайте подсказки само за наистина критични ресурси (2-3 максимум). Прекаленото използване може да засити мрежовата лента, особено при мобилни връзки с по-висока латентност.

Стратегия 3: Приоритизиране на LCP изображението

Тук стигаме до една от най-ефективните и (приятната изненада) лесни за внедряване оптимизации. Само 15% от уебсайтовете използват атрибута fetchpriority за приоритизиране на LCP ресурса. Това означава огромно конкурентно предимство за тези, които го правят.

Атрибутът fetchpriority

По подразбиране браузърите зареждат изображения с нисък приоритет, тъй като те не блокират рендирането. Едва след layout-а, ако изображението е във viewport-а, приоритетът се повишава. С fetchpriority="high" можете да прескочите тази стъпка:

<!-- LCP изображение с висок приоритет -->
<img
  src="/images/hero.webp"
  alt="Hero банер"
  width="1200"
  height="600"
  fetchpriority="high"
>

<!-- Останалите изображения под fold-а -->
<img
  src="/images/feature-1.webp"
  alt="Функция 1"
  loading="lazy"
  width="400"
  height="300"
>

Само тази промяна може да намали LCP с 300–800 ms. Един атрибут, толкова голяма разлика. Браузърната поддръжка вече е отлична: Chrome 102+, Safari 17.2+ и Firefox 132+.

Preload за LCP ресурса

За максимален ефект, комбинирайте fetchpriority с preload в <head> секцията:

<head>
  <!-- Preload на LCP изображението с висок приоритет -->
  <link
    rel="preload"
    href="/images/hero.webp"
    as="image"
    fetchpriority="high"
    type="image/webp"
  >

  <!-- За responsive изображения -->
  <link
    rel="preload"
    as="image"
    fetchpriority="high"
    imagesrcset="/images/hero-400.webp 400w,
                 /images/hero-800.webp 800w,
                 /images/hero-1200.webp 1200w"
    imagesizes="100vw"
  >
</head>

Важно: ако използвате preload за responsive изображение, задължително добавете imagesrcset и imagesizes атрибутите, за да се зареди правилният размер за текущия viewport. Пропуснете src атрибута, за да не зареждате фолбек изображението в браузъри, които не поддържат responsive preloading.

Критична грешка: lazy loading на LCP изображението

Една от най-честите грешки, които виждам, е прилагането на lazy loading върху LCP елемента. Когато добавите loading="lazy" на изображение, вие изрично казвате на браузъра да го деприоритизира. Ако това е вашият LCP елемент — резултатът е катастрофален.

JavaScript базираните библиотеки за lazy loading (като lazysizes.js) са дори по-лоши. Те добавят допълнително забавяне за изтегляне и изпълнение на JS, преди изобщо да се стартира зареждането на изображението.

<!-- ГРЕШНО: lazy loading на LCP изображение -->
<img src="/hero.webp" loading="lazy" alt="Hero">

<!-- ПРАВИЛНО: eager loading с висок приоритет -->
<img src="/hero.webp" loading="eager" fetchpriority="high" alt="Hero">

Стратегия 4: Оптимизация на изображения

Макар че Resource Load Duration рядко е основното тесно място, оптимизирането на размера на изображенията все пак е важно — особено за потребители с бавни мобилни връзки.

Модерни формати: WebP и AVIF

AVIF предлага средно 50% по-малък размер спрямо JPEG при същото визуално качество. WebP е с около 25–35% по-малък. Използвайте <picture> елемента за прогресивно подобряване:

<picture>
  <!-- AVIF за браузъри, които го поддържат -->
  <source
    srcset="/images/hero.avif"
    type="image/avif"
  >
  <!-- WebP като фолбек -->
  <source
    srcset="/images/hero.webp"
    type="image/webp"
  >
  <!-- JPEG за стари браузъри -->
  <img
    src="/images/hero.jpg"
    alt="Hero банер"
    width="1200"
    height="600"
    fetchpriority="high"
  >
</picture>

Responsive изображения с srcset

Изпращането на 2000px изображение на мобилен телефон е чиста загуба на честотна лента. Просто няма причина за това. Използвайте srcset, за да сервирате правилния размер:

<img
  srcset="/images/hero-400.webp 400w,
          /images/hero-800.webp 800w,
          /images/hero-1200.webp 1200w,
          /images/hero-1600.webp 1600w"
  sizes="(max-width: 768px) 100vw,
         (max-width: 1200px) 80vw,
         1200px"
  src="/images/hero-1200.webp"
  alt="Hero банер"
  width="1200"
  height="600"
  fetchpriority="high"
>

Формулата за sizes е проста: (рендиран размер / ширина на viewport) × 100. Ако hero изображението заема цялата ширина на мобилния екран, използвайте 100vw. Ако заема 80% от десктоп екрана — 80vw.

Автоматизация с build инструменти

Ръчното генериране на множество размери и формати е непрактично (и досадно, нека бъдем честни). Ето примерна конфигурация с Vite и vite-imagetools:

// vite.config.js
import { defineConfig } from 'vite';
import { imagetools } from 'vite-imagetools';

export default defineConfig({
  plugins: [
    imagetools({
      defaultDirectives: (url) => {
        if (url.searchParams.has('hero')) {
          return new URLSearchParams({
            format: 'avif;webp;jpg',
            width: '400;800;1200;1600',
            quality: '80',
          });
        }
        return new URLSearchParams();
      },
    }),
  ],
});

Стратегия 5: Елиминиране на блокиращи ресурси

Render-блокиращите ресурси — CSS и JavaScript файлове, които спират браузъра от визуализация на съдържанието, докато бъдат напълно изтеглени и обработени. Те пряко влияят както на Resource Load Delay, така и на Element Render Delay.

Инлайн критичен CSS

Вместо да чакате целия CSS файл, извлечете критичните стилове (тези, необходими за above-the-fold съдържанието) и ги вградете директно в HTML-а:

<head>
  <!-- Критичен CSS вграден директно -->
  <style>
    /* Само стиловете за above-the-fold */
    .hero { position: relative; width: 100%; height: 60vh; }
    .hero img { width: 100%; height: 100%; object-fit: cover; }
    .hero-title { font-size: 2.5rem; color: #fff; }
    nav { display: flex; align-items: center; padding: 1rem; }
  </style>

  <!-- Останалият CSS се зарежда асинхронно -->
  <link
    rel="preload"
    href="/styles/main.css"
    as="style"
    onload="this.onload=null;this.rel='stylesheet'"
  >
  <noscript>
    <link rel="stylesheet" href="/styles/main.css">
  </noscript>
</head>

Инструменти като critical (npm пакет) или critters (за webpack/Vite) могат автоматично да извлекат критичния CSS при build процеса. Спестяват ви доста ръчна работа.

Отлагане на некритичен JavaScript

Използвайте defer или async за скриптове, които не са необходими за първоначалната визуализация:

<!-- Критичен скрипт: зарежда се нормално, но е минимален -->
<script src="/js/critical.js"></script>

<!-- Некритични скриптове: отложени -->
<script defer src="/js/analytics.js"></script>
<script defer src="/js/chat-widget.js"></script>

<!-- Скриптове на трети страни: зареждат се след onload -->
<script>
  window.addEventListener('load', () => {
    const script = document.createElement('script');
    script.src = 'https://third-party.example.com/widget.js';
    document.body.appendChild(script);
  });
</script>

Стратегия 6: SSR, SSG и стрийминг рендиране

Client-Side Rendering (CSR) е един от най-големите врагове на LCP. При CSR браузърът получава празен HTML, изтегля JavaScript, изпълнява го и едва тогава генерира съдържанието. Резултатът? Огромен Resource Load Delay.

Преминаване към SSR или SSG

Преминаването от CSR към Server-Side Rendering (SSR) или Static Site Generation (SSG) обикновено намалява LCP с 40–60%. Причината е проста — LCP елементът присъства директно в HTML-а, което го прави видим за preload скенера на браузъра от самото начало.

Стрийминг SSR с React Server Components

Стрийминг SSR изпраща HTML към браузъра преди цялата страница да е готова. С Next.js и React Server Components можете да приоритизирате LCP елемента:

// app/page.tsx (Next.js App Router)
import { Suspense } from 'react';

// Hero секцията се рендира незабавно на сървъра
function HeroSection() {
  return (
    <section className="hero">
      <img
        src="/images/hero.webp"
        alt="Hero банер"
        width={1200}
        height={600}
        fetchPriority="high"
      />
      <h1>Добре дошли</h1>
    </section>
  );
}

// Тежкото съдържание се зарежда по-късно
async function ProductList() {
  const products = await fetchProducts();
  return <div>{/* Рендиране на продуктите */}</div>;
}

export default function Page() {
  return (
    <>
      {/* LCP секцията е извън Suspense — изпраща се веднага */}
      <HeroSection />

      {/* Останалото съдържание се стриймва по-късно */}
      <Suspense fallback={<p>Зареждане...</p>}>
        <ProductList />
      </Suspense>
    </>
  );
}

Ключовото е, че LCP секцията е извън Suspense boundary-то, което гарантира, че тя е част от първоначалния HTML отговор. Тежките данни (списък продукти, коментари и т.н.) се стриймват по-късно, без да забавят LCP.

Стратегия 7: Оптимизация на HTML структурата за LCP откритие

Ето нещо, за което не се говори достатъчно: preload скенерът на браузъра обработва HTML документа от горе надолу. Ако некритични, но тежки ресурси (икони в хедъра, скриптове за чат уиджети) са позиционирани по-високо в <body> от LCP елемента, те се откриват и добавят в опашката за изтегляне първи — заемайки мрежова лента и забавяйки LCP ресурса.

Проблемът с големия HTML

Голям HTML документ също може да е проблем. Ако LCP елементът не е в първия пакет данни, който браузърът получава (около 14KB), неговото откриване се забавя с поне един мрежов round trip. Затова пазете началото на HTML-а чисто и компактно.

Фонови изображения срещу <img> тагове

CSS фоновите изображения (background-image) са невидими за preload скенера — те биват открити едва след като CSS-ът бъде изтеглен и парснат, което е значително по-бавно. Решението е просто: използвайте обикновен <img> таг с object-fit: cover, за да имитирате визуално поведението на фоново изображение:

<!-- Вместо CSS background-image -->
<style>
  .hero-wrapper { position: relative; width: 100%; height: 60vh; overflow: hidden; }
  .hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1; }
</style>

<div class="hero-wrapper">
  <img
    class="hero-bg"
    src="/images/hero-bg.webp"
    alt=""
    width="1920"
    height="1080"
    fetchpriority="high"
  >
  <div class="hero-content">
    <h1>Вашето заглавие тук</h1>
  </div>
</div>

Стратегия 8: Оптимизация на уеб шрифтове за текстов LCP

Когато LCP елементът е текст (заглавие или голям параграф), уеб шрифтовете могат да бъдат основният виновник. По подразбиране повечето браузъри скриват текста, докато уеб шрифтът се зарежда — така нареченият FOIT (Flash of Invisible Text).

font-display: swap и optional

/* Показване на системен шрифт веднага, подмяна при зареждане */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;
}

/* За по-малко важни шрифтове: зареждане само при бърза връзка */
@font-face {
  font-family: 'DecorativeFont';
  src: url('/fonts/decorative.woff2') format('woff2');
  font-display: optional;
}

Preload на критични шрифтове

<head>
  <link
    rel="preload"
    href="/fonts/custom.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  >
</head>

Комбинацията от font-display: swap и preload гарантира, че текстът е видим веднага (с фолбек шрифт), а визуалната подмяна става максимално бързо. Просто работи.

Стратегия 9: Мобилна оптимизация

Мобилните процесори отнемат 3–5 пъти повече време за парсване и изпълнение на JavaScript в сравнение с десктоп. А тъй като Google използва мобилната версия на сайта за определяне на класирането, мобилната оптимизация не е просто „хубаво да се направи" — тя е от критично значение.

Различни LCP стратегии за мобилно и десктоп

<picture>
  <!-- Мобилно: по-малко изображение, оптимизирано за тесен екран -->
  <source
    media="(max-width: 768px)"
    srcset="/images/hero-mobile.avif"
    type="image/avif"
  >
  <source
    media="(max-width: 768px)"
    srcset="/images/hero-mobile.webp"
    type="image/webp"
  >
  <!-- Десктоп: пълноразмерно изображение -->
  <source
    srcset="/images/hero-desktop.avif"
    type="image/avif"
  >
  <source
    srcset="/images/hero-desktop.webp"
    type="image/webp"
  >
  <img
    src="/images/hero-desktop.jpg"
    alt="Hero банер"
    width="1200"
    height="600"
    fetchpriority="high"
  >
</picture>

Понякога по-прост мобилен layout с по-малко ресурси работи по-добре от responsive дизайн, който се опитва да направи всичко. Помислете дали мобилната версия наистина се нуждае от hero изображение на цял екран — може би по-компактна секция е напълно достатъчна.

Диагностика: Как да идентифицирате вашия LCP елемент

Преди да оптимизирате каквото и да е, трябва да знаете кой точно е вашият LCP елемент. Едно от най-честите грешки е оптимизирането на грешен елемент — вярвайте ми, виждал съм го не веднъж.

С Chrome DevTools

  1. Отворете DevTools (F12)
  2. Отидете в Performance панела
  3. Натиснете Record и презаредете страницата
  4. В Timings секцията потърсете LCP маркера
  5. Кликнете върху него — DevTools ще маркира LCP елемента в DOM дървото

Програмно чрез JavaScript

new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];

  console.log('LCP елемент:', lastEntry.element);
  console.log('LCP стойност:', lastEntry.renderTime || lastEntry.loadTime, 'ms');
  console.log('LCP URL:', lastEntry.url || '(текстов елемент)');
  console.log('LCP размер:', lastEntry.size);
}).observe({ type: 'largest-contentful-paint', buffered: true });

С web-vitals библиотеката

import { onLCP } from 'web-vitals';

onLCP((metric) => {
  console.log('LCP стойност:', metric.value);
  console.log('LCP рейтинг:', metric.rating); // good, needs-improvement, poor

  // Детайли за LCP елемента
  const entry = metric.entries[metric.entries.length - 1];
  console.log('LCP елемент:', entry.element?.tagName);
  console.log('LCP URL:', entry.url);
});

Чест проблем: Верижни заявки (Request Chains)

Верижните заявки възникват, когато зареждането на LCP ресурса зависи от предварително изтегляне на друг ресурс. Класически пример: CSS файл → CSS @import → фоново изображение. Всяка стъпка добавя мрежов round trip и драгоценни милисекунди.

Как да съкратите веригата

  • Избягвайте CSS @import — използвайте <link> тагове за всеки CSS файл
  • Хостирайте критичните ресурси на същия origin — избягвайте нови DNS/TCP/TLS връзки
  • Използвайте preconnect за external origins, които не можете да избегнете
  • Използвайте preload за ресурси, които иначе биха били открити късно
<head>
  <!-- Предварително свързване с CDN за изображения -->
  <link rel="preconnect" href="https://cdn.example.com">

  <!-- Директен preload вместо CSS @import верига -->
  <link rel="preload" href="https://cdn.example.com/hero.webp" as="image" fetchpriority="high">

  <!-- CSS файлове паралелно, не чрез @import -->
  <link rel="stylesheet" href="/styles/reset.css">
  <link rel="stylesheet" href="/styles/main.css">
</head>

Мониторинг и валидиране на подобренията

Оптимизацията не свършва с внедряването — всъщност, тогава тепърва започва истинската работа. Трябва да проследявате резултатите с реални потребителски данни (RUM), а не само с лабораторни тестове.

Разликата между лабораторни и полеви данни

Вашият Lighthouse резултат може да изглежда отлично, но ако реалните потребители на бавни връзки виждат LCP от 5 секунди, CrUX данните ще показват провал. Затова:

  • PageSpeed Insights — комбинира лабораторни (Lighthouse) и полеви (CrUX) данни
  • Google Search Console — показва Core Web Vitals за вашите страници в реалния свят
  • RUM инструменти — DebugBear, SpeedCurve, Vercel Analytics или собствено решение с web-vitals

Настройване на автоматичен мониторинг

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

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id,
    navigationType: metric.navigationType,
    url: window.location.href,
    userAgent: navigator.userAgent,
    effectiveType: navigator.connection?.effectiveType,
    deviceMemory: navigator.deviceMemory,
  });

  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics/vitals', body);
  } else {
    fetch('/analytics/vitals', { body, method: 'POST', keepalive: true });
  }
}

// Проследяване на всичките три Core Web Vitals
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

Контролен списък за LCP оптимизация

И така, нека обобщим всичко в едно място. Ето списъкът с всички стратегии, подредени по приоритет и въздействие:

  1. Идентифицирайте LCP елемента — използвайте DevTools или web-vitals, за да знаете какво оптимизирате
  2. Измерете LCP подкомпонентите — разберете къде се губи времето (TTFB, Load Delay, Load Duration, Render Delay)
  3. Оптимизирайте TTFB — CDN, сървърно кеширане, stale-while-revalidate
  4. Внедрете HTTP 103 Early Hints — ако CDN-ът ви го поддържа
  5. Добавете fetchpriority="high" — на LCP изображението
  6. Preload на LCP ресурса — в <head> секцията
  7. Махнете lazy loading от LCP изображението — използвайте loading="eager"
  8. Конвертирайте в WebP/AVIF — намалете размера на файла
  9. Сервирайте responsive изображения — чрез srcset и sizes
  10. Инлайнете критичен CSS — и заредете останалия асинхронно
  11. Използвайте <img> вместо background-image за LCP — за бързо откриване от preload скенера
  12. Преминете към SSR/SSG — ако използвате CSR
  13. Оптимизирайте уеб шрифтовете — font-display: swap + preload
  14. Съкратете верижните заявки — preconnect, preload, избягвайте @import
  15. Мониторирайте с RUM данни — не разчитайте само на лабораторни тестове

Заключение: LCP е марафон, не спринт

Оптимизацията на Largest Contentful Paint не е еднократно действие — това е непрекъснат процес. Нови функционалности, промени в съдържанието и актуализации на зависимости могат да влошат LCP по всяко време. Затова автоматичният мониторинг и системата за ранно предупреждение са толкова важни.

Добрата новина? Повечето оптимизации, които разгледахме, са сравнително лесни за внедряване и носят значителни подобрения. Добавянето на fetchpriority="high" към LCP изображението е буквално промяна на един атрибут, но може да намали LCP с до 800 ms. HTTP 103 Early Hints могат да подобрят LCP с 30% и повече, а преминаването от CSR към SSR — с 40–60%.

Заедно с оптимизацията на INP, която разгледахме в предишното ръководство, вече имате пълна картина за две от трите Core Web Vitals. Следващата стъпка? Cumulative Layout Shift (CLS) — но това е тема за друг път.

Започнете с измерването на вашите LCP подкомпоненти днес. Идентифицирайте къде точно се губи времето и приложете подходящата стратегия. Резултатите ще дойдат по-бързо, отколкото очаквате.

За Автора Editorial Team

Our team of expert writers and editors.