2 lines
13 KiB
JSON
2 lines
13 KiB
JSON
{"index":2,"slug":"editorial-2027-12-mechanism-author-manifesto","title":"Web performance budget: как читать LCP, INP и CLS вместе","excerpt":"Разбираем, почему один показатель не объясняет скорость интерфейса, как разделить LCP, INP и CLS и превратить наблюдение в проверяемое действие.","contentHtml":"<p>Проблема начинается с отчёта «страница медленная». Такой симптом не говорит, что именно увидел пользователь: крупнейший блок появился поздно, клик обработался с задержкой или контент прыгнул под пальцем. Цена ошибки — чинить не тот участок: уменьшить JavaScript, пока главный баннер ждёт шрифт, или ускорить загрузку, оставив интерфейс заблокированным длинной задачей.</p>\n<p>Причина — свести LCP, INP и CLS к одному среднему score. Эти метрики отвечают на разные вопросы и требуют разных разрезов данных. LCP описывает появление крупнейшего видимого элемента, INP — задержку взаимодействия, CLS — неожиданные сдвиги layout. Сначала нужно определить тип симптома и границу измерения, потом выбирать изменение в коде.</p>\n<h2>Тезис: performance budget состоит из нескольких проверяемых границ</h2>\n<p>Budget — это не красивое число в дашборде. Это набор порогов, сегментов, периода и действий владельца. Для каждого показателя нужно знать, какую часть опыта он описывает, где искать причину и что считать регрессией. Рекомендованные границы Core Web Vitals — LCP до 2500 мс, INP до 200 мс и CLS до 0,1. Они помогают классифицировать опыт, но не доказывают причину.</p>\n<p>Для полевых данных используйте percentile, а не только среднее. Например, p75 показывает значение, ниже которого находится 75 процентов наблюдений в выбранном сегменте. Смешивать в одной строке мобильные и десктопные устройства, разные версии и разные периоды нельзя: итог потеряет смысл. В budget явно запишите URL, устройство, соединение, release и окно наблюдения.</p>\n<h2>Механизм: три метрики — три объекта диагностики</h2>\n<p>LCP фиксирует момент, когда крупнейший контентный элемент стал видимым в пределах загрузки. На результат влияют TTFB, критический CSS, изображение, шрифт, сеть и работа браузера. Поэтому плохой LCP — сигнал разобрать цепочку, а не команда сразу добавить preload или сжать картинку.</p>\n<p>INP оценивает задержку после взаимодействий пользователя. Ищите конкретное interaction, обработчик и long task на main thread. Причиной может быть тяжёлый обработчик, сторонний скрипт, лишняя синхронная работа или слабое устройство. Быстрый LCP не означает, что интерфейс быстро отвечает после ввода.</p>\n<p>CLS суммирует неожиданные сдвиги layout. Типовые источники — изображение без зарезервированных размеров, поздняя реклама, вставленный сверху контент или изменение шрифта. Для проверки нужен shifted element и момент сдвига. Удаление одного долгого скрипта не исправит CLS, если браузер по-прежнему не знает размеры блока.</p>\n<table><caption>Как переводить симптом в проверку</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Вероятная причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>LCP выше 2500 мс</td><td>TTFB, ресурс, CSS или шрифт</td><td>element timing, TTFB, waterfall</td><td>Ускорить критический путь и повторить замер</td></tr><tr><td>INP выше 200 мс</td><td>long task или тяжёлый handler</td><td>interaction trace и main thread</td><td>Разбить работу или перенести её после ответа</td></tr><tr><td>CLS выше 0,1</td><td>Нет места под изображение или вставку</td><td>shifted element и layout trace</td><td>Зарезервировать размер и стабилизировать layout</td></tr><tr><td>Среднее хорошее, p75 плохой</td><td>Длинный хвост или смешанные сегменты</td><td>p75 по URL, устройству и release</td><td>Разделить сегменты и назначить владельца</td></tr><tr><td>Lab и field расходятся</td><td>Разная среда и состав трафика</td><td>Сопоставить условия запуска</td><td>Не подменять один тип данных другим</td></tr></tbody></table>\n<h2>Минимальный рабочий пример</h2>\n<p>Классификатор ниже принимает уже собранные значения и возвращает статус каждой метрики. Он показывает механику budget, но не собирает браузерные данные и не считает percentile. Реальные значения нужно получать через подходящие API наблюдения, сохранять с контекстом и агрегировать по сегментам.</p>\n<pre><code>function classifyWebVitals({ lcpMs, inpMs, cls }) {\n if (![lcpMs, inpMs, cls].every((value) => Number.isFinite(value) && value >= 0)) {\n return { ok: false, reason: 'vital-input-invalid' };\n }\n\n const lcp = lcpMs <= 2500 ? 'good' : lcpMs <= 4000 ? 'needs-improvement' : 'poor';\n const inp = inpMs <= 200 ? 'good' : inpMs <= 500 ? 'needs-improvement' : 'poor';\n const layout = cls <= 0.1 ? 'good' : cls <= 0.25 ? 'needs-improvement' : 'poor';\n const statuses = [lcp, inp, layout];\n const overall = statuses.includes('poor') ? 'poor'\n : statuses.includes('needs-improvement') ? 'needs-improvement'\n : 'good';\n\n return { ok: true, lcp, inp, layout, overall };\n}\n\nconsole.log(classifyWebVitals({ lcpMs: 2180, inpMs: 240, cls: 0.08 }));\n// { ok: true, lcp: 'good', inp: 'needs-improvement', layout: 'good', overall: 'needs-improvement' }</code></pre>\n<p>Порог в функции нужен для triage. Он не отвечает на вопрос «какой код виноват». Даже улучшение LCP после preload не доказывает, что preload был единственной причиной: могли измениться сервер, кэш или состав трафика.</p>\n<figure><img src=\"/assets/editorial/2027/author-manifesto-2027-quality-rubric-matrix.svg\" alt=\"Матрица web-performance связывает LCP, INP и CLS с разными объектами измерения, порогами и диагностическими разрезами\" loading=\"lazy\" /><figcaption>Один score скрывает разные механизмы. Для каждой метрики нужен собственный объект проверки и действие.</figcaption></figure>\n<h2>Порядок работы</h2>\n<ol><li>Определите URL, сегмент, percentile и окно наблюдения. Не сравнивайте p75 мобильного трафика со средним по всем устройствам.</li><li>Для плохого LCP найдите element и разделите TTFB, загрузку ресурса и отрисовку. Для INP найдите interaction и long task. Для CLS найдите shifted element.</li><li>Сформулируйте один budget на релиз и один диагностический сигнал. Не блокируйте сборку по метрике, которую CI не может воспроизвести.</li><li>Сопоставьте лабораторные и полевые данные отдельно. Lighthouse удобен для воспроизводимого CI, field data показывает реальное разнообразие сети и устройств.</li><li>Измените один тяжёлый участок: критический ресурс, обработчик, размеры изображения или резервирование места.</li><li>Повторите измерение тем же сегментом и окном. Улучшение LCP не закрывает автоматически INP и CLS.</li></ol>\n<h2>Ограничения и критерий готовности</h2>\n<p>Локальный запуск классификатора не является field evidence. W3C LCP и Performance Timeline описывают API и объекты измерения, но не проверяют вашу агрегацию, sampling или дашборд. Порог web.dev — практическая рекомендация, а не гарантия UX и не причинная модель. Внешние скрипты, кэш, браузер, сеть и состав аудитории могут изменить результат.</p>\n<p>Оптимизация одной метрики может ухудшить другую. Сжатие изображения уменьшает передаваемый объём, но может добавить CPU-декодирование или снизить качество. Разделение JavaScript способно помочь INP, но добавить запросы и повлиять на LCP. Рядом с Core Web Vitals проверяйте размер ресурсов, long tasks и ошибки.</p>\n<p>Готовность наступает, когда для выбранного URL есть p75 LCP, INP и CLS по явно названным сегментам; для каждой плохой строки указан element, interaction или shifted element; изменение повторено тем же способом; соседние метрики не ухудшились. Если одного поля нет, вывод нужно назвать неполным, а не превращать его в общий score.</p>\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://www.w3.org/TR/largest-contentful-paint/\" target=\"_blank\" rel=\"noopener noreferrer\">W3C Largest Contentful Paint</a> — определение LCP и объекта наблюдения. Граница: Working Draft может изменяться; LCP не измеряет всю скорость страницы, отзывчивость или стабильность layout.</li><li><a href=\"https://www.w3.org/TR/2025/CRD-performance-timeline-20250521/\" target=\"_blank\" rel=\"noopener noreferrer\">W3C Performance Timeline</a> — API и измеряемые entry для браузерных наблюдений. Граница: спецификация не задаёт ваши пороги, backend-агрегацию и причинность медленной страницы.</li><li><a href=\"https://web.dev/articles/vitals\" target=\"_blank\" rel=\"noopener noreferrer\">Web Vitals — web.dev</a> — рекомендованные пороги LCP, INP и CLS и правило p75 по сегментам. Граница: это guidance, а не гарантия UX и не доказательство причины конкретной регрессии.</li></ul>"}
|