Files
progcode/editorial/agent-rewrites/002.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

2 lines
13 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"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) =&gt; Number.isFinite(value) &amp;&amp; value &gt;= 0)) {\n return { ok: false, reason: 'vital-input-invalid' };\n }\n\n const lcp = lcpMs &lt;= 2500 ? 'good' : lcpMs &lt;= 4000 ? 'needs-improvement' : 'poor';\n const inp = inpMs &lt;= 200 ? 'good' : inpMs &lt;= 500 ? 'needs-improvement' : 'poor';\n const layout = cls &lt;= 0.1 ? 'good' : cls &lt;= 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>"}