Улучшение Core Web Vitals на практике — Как сократить LCP с 3 секунд до 1.2 секунды
USD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。
Краткое содержание
- LCP (Largest Contentful Paint) — это ключевой показатель, который напрямую влияет на рейтинг в поиске Google.
- Мы последовательно применили три метода: оптимизацию изображений, стратегию загрузки шрифтов и код-сплиттинг, чтобы сократить LCP с 3.0 секунд до 1.2 секунд.
- CLS был снижен с 0.28 до 0.04 за счет явного указания размеров для изображений и рекламных областей, а также устранения изменений в макете.
- Это основано на реальном проекте Next.js, и те же принципы можно применить к проектам на React и Vue.
Введение — Почему Core Web Vitals важны?
В 2021 году Google выпустил обновление Page Experience, и Core Web Vitals стали не просто показателями UX, а элементами ранжирования SEO. С тех пор Google постоянно увеличивает вес этих сигналов, и к 2024 году INP (Interaction to Next Paint) полностью заменит FID, что также изменит критерии оценки.
Мой проект электронной коммерции еще полгода назад имел ужасный мобильный рейтинг в 38 баллов по PageSpeed Insights. LCP составлял 3.0 секунды, CLS — 0.28, INP — 320 мс — все три показателя находились в зоне "требуется улучшение". В этой статье я расскажу, как я изменил эти показатели и зафиксирую весь процесс.
Как быстро понять три показателя Core Web Vitals?
LCP — скорость "первого впечатления" страницы
LCP — это время, необходимое для рендеринга самого большого элемента контента в области просмотра (обычно это изображение героя или текст H1). По стандартам Google хорошим считается значение ниже 2.5 секунд, а плохим — выше 4.0 секунд.
На сайтах электронной коммерции чаще всего изображение баннера героя является объектом LCP. Если это изображение не загружается вовремя, все остальные оптимизации становятся бессмысленными.
CLS — не "прыгает" ли макет?
CLS (Cumulative Layout Shift) измеряет степень неожиданного перемещения элементов во время загрузки страницы. Если рекламный баннер вставляется слишком поздно или изображение загружается без указания размеров, показатель увеличивается. Хорошим считается значение ниже 0.1.
INP — насколько быстро реагирует на пользовательский ввод?
INP измеряет задержку ответа на все взаимодействия, такие как клики, нажатия и ввод с клавиатуры. В марте 2024 года он заменил FID, и хорошим считается значение ниже 200 мс. Чем тяжелее JavaScript-бандлы, тем больше блокируется основной поток, что ухудшает INP.
Диагностика проблемы — Первоначальные измерения
Точность записи показателей до оптимизации — это отправная точка. Я использовал следующие инструменты по порядку:
- 1PageSpeed Insights — одновременно предоставляет полевые данные (данные реальных пользователей) и лабораторные данные (Lighthouse)
- 2Chrome DevTools > Вкладка Performance — проверка временной шкалы рендеринга и кандидатов на LCP
- 3WebPageTest — реальное измерение на основе CDN-узлов в стране, визуальная проверка с помощью фильмстрима
- 4Vercel Analytics / Sentry — сбор Core Web Vitals на основе реальных пользовательских сессий
По результатам диагностики узкие места были определены в трех местах.
- Изображение героя — 4.2MB JPEG, вставлено напрямую с помощью тега
без оптимизации - Google Fonts — синхронная загрузка 3-х семейств шрифтов с помощью
@import - Размер бандла — основной чанк 1.8MB, включает множество библиотек без применения tree-shaking
Как сократить LCP с 3.0 секунд до 1.2 секунд?
Метод 1 — Оптимизация изображений и preload
Это дало наибольший эффект. Я полностью изменил способ обработки изображения героя.
До
<img src="/banner.jpg" alt="Главный баннер" />После — компонент Image Next.js + конвертация в WebP
import Image from 'next/image';
<Image
src="/banner.webp"
alt="Главный баннер"
width={1920}
height={800}
priority // Изображение, являющееся объектом LCP, должно быть с приоритетом
quality={80}
sizes="(max-width: 768px) 100vw, 1920px"
/>Добавление только свойства priority позволяет Next.js автоматически вставить тег для этого изображения. После конвертации в WebP размер файла уменьшился с 4.2MB до 340KB, что составляет примерно 92% уменьшения.
Если у вас чистый проект HTML/React, вы можете добавить тег preload напрямую в .
<link
rel="preload"
as="image"
href="/banner.webp"
type="image/webp"
/>Эта работа позволила снизить LCP с 3.0 секунд до 1.8 секунд.
Метод 2 — Изменение стратегии загрузки шрифтов
Если загружать Google Fonts с помощью @import, браузер останавливается на парсинге CSS и отправляет запрос на шрифты. Это время ожидания значительно увеличивает LCP.
До
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap');
</style>После — + display=swap + собственный хостинг
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap"
/>Более того, используя next/font (Next.js 13+), можно обрабатывать шрифты на этапе сборки, что полностью устраняет внешние запросы.
// app/layout.js
import { Noto_Sans_KR } from 'next/font/google';
const notoSansKR = Noto_Sans_KR({
weight: ['400', '700'],
subsets: ['latin'],
display: 'swap',
});После улучшения шрифтов LCP снизился с 1.8 секунд до 1.5 секунд.
Метод 3 — Код-сплиттинг и оптимизация бандла
Если основной JS-бандл слишком велик, основной поток браузера блокируется на парсинге и выполнении. В это время элемент LCP не отображается на экране.
Динамический импорт для разделения начального бандла
import dynamic from 'next/dynamic';
// Тяжелый компонент графика, который находится ниже по прокрутке
const SalesChart = dynamic(() => import('@/components/SalesChart'), {
ssr: false,
loading: () => <div className="chart-skeleton" />,
});Анализ бандла для удаления ненужных библиотек
npx @next/bundle-analyzerВ результате анализа было обнаружено, что включены moment.js (66KB gzip) и вся библиотека lodash (70KB gzip). Я заменил moment на необходимые функции из date-fns, а lodash переключил на импорт с tree-shaking из lodash-es.
// До
import _ from 'lodash';
const result = _.groupBy(data, 'category');
// После
import { groupBy } from 'lodash-es';
const result = groupBy(data, 'category');Основной бандл уменьшился с 1.8MB до 420KB, а LCP снизился с 1.5 секунд до 1.2 секунд.
Снижение CLS с 0.28 до 0.04
Основными виновниками перемещения макета были два момента.
1. Изображения без указанных размеров
Я указал width и height для всех тегов , или зафиксировал aspect-ratio с помощью CSS.
.product-image-wrapper {
aspect-ratio: 4 / 3;
overflow: hidden;
}2. Поздно вставляемые рекламные баннеры
Я заранее зафиксировал плейсхолдер одинаковой высоты до загрузки рекламного слота.
<div style={{ minHeight: '90px' }}>
<AdBanner slot="header" />
</div>Эти два изменения позволили снизить CLS с 0.28 до 0.04.
Улучшение INP — снижение нагрузки на основной поток
INP естественным образом улучшился после уменьшения бандла с 320 мс до 140 мс. Дополнительно я отложил тяжелые вычисления внутри обработчика событий клика с помощью requestIdleCallback.
button.addEventListener('click', () => {
// Немедленное обновление UI
setLoading(true);
// Тяжелая обработка откладывается на время простоя
requestIdleCallback(() => {
processHeavyData(payload);
});
});Сравнение до и после оптимизации
| Показатель | До оптимизации | После оптимизации |
|---|
🔧 Related Free Tools
Похожее
USD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。...
IT6 способов зарабатывать дополнительный доход с ChatGPT — практическое и проверенное руководство по монетизации на 2026 годUSD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。...
IT2026 ChatGPT vs Claude vs Gemini — Сравнение производительности, цен и способов использования AI-чат-ботовUSD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。...
ITОптимизация скорости сайта в 2026 году — как достичь Core Web Vitals 90+USD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。...