Добавляет статью о Core Web Vitals - #6101
Conversation
|
Проконсультироваля с @furtivite - переношу в секцию "Инструменты" в "Веб-платформа" |
Превью контента из ad92927 опубликовано. |
vitya-ne
left a comment
There was a problem hiding this comment.
Привет, спасибо за такую подробную статью!
Добавил несколько коментов. Пока лучше не исправляй - я просмотрел около четверти (Ооочень большой пиар).
Когда закончу, напишу.
| @@ -0,0 +1,4 @@ | |||
| { | |||
There was a problem hiding this comment.
Этот файл добавлять не нужно, он создаётся автоматически.
| - article | ||
| --- | ||
|
|
||
| Попробуйте открыть любой веб-сайт. Между кликом по ссылке и моментом, когда сайтом можно пользоваться, браузер получает данные, загружает изображения, стили и скрипты, а затем отрисовывает страницу. На любом этапе может возникнуть задержка: главный контент появляется слишком поздно, элементы прыгают, а кнопки не сразу реагируют на нажатие. Все это производительность и очень долгое время не существовало понятных метрик, как можно сравнить сайты между собой и как понять, что нужно улучшать разработчикам. |
There was a problem hiding this comment.
Все это производительность и очень долгое время не существовало..
- Тут не хватает глагола, чтобы понять мысль: производительность низкая ? производительность чего ?
|
|
||
| ## Зачем Google ввёл эти метрики | ||
|
|
||
| У производительности сайтов существуют две стороны, и обе важны бизнесу: |
There was a problem hiding this comment.
и обе важны бизнесу:
Может стоит два следующих абзаца сделать элементами ненумерованного списка ?
|
|
||
| **Пользовательский опыт.** Медленный и нестабильный сайт заставляет людей уходить, не дойдя до просмотра, заявки или заказа. Core Web Vitals выражают в понятных показателях три главных раздражителя: долгую загрузку, медленную реакцию на действия и прыжки элементов. | ||
|
|
||
| **SEO.** Google встроил Core Web Vitals в сигнал ранжирования Page Experience. При этом Google оценивает метрики не по лабораторным тестам, а по данным реальных пользователей Chrome из отчёта [CrUX (Chrome User Experience Report)](https://cruxvis.withgoogle.com). То есть поисковик смотрит, как ведёт себя сайт на реальных устройствах пользователей. |
There was a problem hiding this comment.
То есть поисковик смотрит, как ведёт себя сайт на реальных устройствах пользователей.
Мысль ясна, но она немного пугает. "Смотрит" всё-таки браузер, а не поисковик, да ?
Может стоит поменять на:
"То есть поисковик анализирует, как ведёт себя сайт на реальных устройствах пользователей."
|
|
||
| **SEO.** Google встроил Core Web Vitals в сигнал ранжирования Page Experience. При этом Google оценивает метрики не по лабораторным тестам, а по данным реальных пользователей Chrome из отчёта [CrUX (Chrome User Experience Report)](https://cruxvis.withgoogle.com). То есть поисковик смотрит, как ведёт себя сайт на реальных устройствах пользователей. | ||
|
|
||
| И сразу нужно запомнить главное: **все Core Web Vitals считаются по 75-му перцентилю**. Метрика считается хорошей только в том случае, если установленному порогу соответствуют не менее 75 % посещений. То есть фокусироваться на быстром сегменте аудитории и не обращать внимания на пользователей с медленными устройствами или со слабым интернетом не получится - Google смотрит именно на «хвост» распределения отдельно для мобильных устройств и десктопных компьютеров |
There was a problem hiding this comment.
| И сразу нужно запомнить главное: **все Core Web Vitals считаются по 75-му перцентилю**. Метрика считается хорошей только в том случае, если установленному порогу соответствуют не менее 75 % посещений. То есть фокусироваться на быстром сегменте аудитории и не обращать внимания на пользователей с медленными устройствами или со слабым интернетом не получится - Google смотрит именно на «хвост» распределения отдельно для мобильных устройств и десктопных компьютеров | |
| И сразу нужно запомнить главное: **все Core Web Vitals считаются по 75-му перцентилю**. Метрика считается хорошей только в том случае, если установленному порогу соответствуют не менее 75 % посещений. То есть фокусироваться на быстром сегменте аудитории и не обращать внимания на пользователей с медленными устройствами или со слабым интернетом не получится - Google смотрит именно на «хвост» распределения отдельно для мобильных устройств и десктопных компьютеров. |
Метрика считается хорошей только в том случае
Может "значение метрики"?
|
|
||
| И сразу нужно запомнить главное: **все Core Web Vitals считаются по 75-му перцентилю**. Метрика считается хорошей только в том случае, если установленному порогу соответствуют не менее 75 % посещений. То есть фокусироваться на быстром сегменте аудитории и не обращать внимания на пользователей с медленными устройствами или со слабым интернетом не получится - Google смотрит именно на «хвост» распределения отдельно для мобильных устройств и десктопных компьютеров | ||
|
|
||
| **Как понять 75-й перцентиль.** Представим 100 посещений сайта и расположим результаты по скорости - от лучших к худшим. Значение на 75-й позиции и будет 75-м перцентилем. Иными словами, установленному порогу должны соответствовать не менее 75 % посещений. Поэтому нельзя ориентироваться только на людей с быстрыми устройствами и стабильным интернетом: Google учитывает и менее удачный пользовательский опыт. |
There was a problem hiding this comment.
Как понять 75-й перцентиль. Представим 100 посещений сайта...
Звучит как заголовок, но выглядит как предложение. Может добавить знак вопроса ?
|
|
||
| Если коротко, три основные метрики отвечают на три простых вопроса: | ||
|
|
||
| - **LCP** (Largest Contentful Paint) - «когда я наконец увидел то, ради чего пришёл в полном составе?» |
There was a problem hiding this comment.
когда я наконец увидел то, ради чего пришёл в полном составе?»
Это игра слов? Не понятно к какой части предложения относится "полный состав". "Я" мог придти не в полном составе ?
|
|
||
| На практике этот этап редко оказывается узким местом: у большинства сайтов основную часть LCP съедают TTFB и задержки, а не сама загрузка файла. Прежде чем оптимизировать именно длительность загрузки, проверьте по полевым данным (CrUX, `web-vitals`), действительно ли это ваше слабое место. | ||
|
|
||
| **Задержка отрисовки**, чтобы скачанное сразу показалось: |
There was a problem hiding this comment.
Задержка отрисовки, чтобы скачанное сразу показалось:
Сбивает.
чтобы "скачанное сразу показалось" - не должно быть задержки.
vitya-ne
left a comment
There was a problem hiding this comment.
@shmakovdima
Добавил ещё несколько коментов и закончил первый раунд ревью.
После прочтения возникло несколько мыслей (делюсь как читатель):
- некоторые подробности можно спрятать в
<details>/<summary>; - возможно, статью можно разбить на несколько (Как измерять и улучшать Core Web Vitals отдельно);
- материала так много, что очень непросто прочитать за один раз.
|
|
||
| #### Углублённая оптимизация | ||
|
|
||
| **Мгновенные переходы между страницами.** Если сайт в основном многостраничный, посмотрите на **Speculation Rules API** - он умеет предзагружать или полностью предрендерить вероятную следующую страницу (например, по наведению на ссылку или по данным аналитики о частых переходах). При переходе она открывается практически мгновенно: LCP такой навигации получается близким к нулю, как при восстановлении из bfcache.</ |
There was a problem hiding this comment.
Speculation Rules API пока не реализован в Firefox и Safari. Возможно стоит предупредить читателя.
|
|
||
| #### Остальные приёмы | ||
|
|
||
| - **[Дебаунсите](/js/debounce) частые события** (`input`, `resize`, `scroll`) - не запускайте тяжёлую логику при каждом событии. Особенно актуально для автокомплита: без дебаунса каждое нажатие клавиши создаёт отдельный обработчик, и они конкурируют за главный поток. Для событий, которые нужно ограничить по частоте, а не отложить до паузы (скролл, resize), больше подходит [троттлинг](/js/throttle); |
There was a problem hiding this comment.
| - **[Дебаунсите](/js/debounce) частые события** (`input`, `resize`, `scroll`) - не запускайте тяжёлую логику при каждом событии. Особенно актуально для автокомплита: без дебаунса каждое нажатие клавиши создаёт отдельный обработчик, и они конкурируют за главный поток. Для событий, которые нужно ограничить по частоте, а не отложить до паузы (скролл, resize), больше подходит [троттлинг](/js/throttle); | |
| - Применяйте **[дебаунсинг](/js/debounce)** к частым событиям (`input`, `resize`, `scroll`) - не запускайте тяжёлую логику при каждом событии. Особенно актуально для автокомплита: без дебаунса каждое нажатие клавиши создаёт отдельный обработчик, и они конкурируют за главный поток. Для событий, которые нужно ограничить по частоте, а не отложить до паузы (скролл, resize), больше подходит [троттлинг](/js/throttle); |
| #### Остальные приёмы | ||
|
|
||
| - **[Дебаунсите](/js/debounce) частые события** (`input`, `resize`, `scroll`) - не запускайте тяжёлую логику при каждом событии. Особенно актуально для автокомплита: без дебаунса каждое нажатие клавиши создаёт отдельный обработчик, и они конкурируют за главный поток. Для событий, которые нужно ограничить по частоте, а не отложить до паузы (скролл, resize), больше подходит [троттлинг](/js/throttle); | ||
| - **Отменяйте устаревшие запросы через [`AbortController`](/js/abort-controller).** Если пользователь быстро печатает в поиске, каждый символ может улетать отдельным [`fetch`](/js/fetch). Отменяйте предыдущий запрос перед отправкой нового - иначе главный поток обрабатывает колбэки ответов, которые уже никому не нужны: |
There was a problem hiding this comment.
каждый символ может улетать отдельным
fetch
По-моему, слишком образно - fetch() ведь не самолёт.
Я бы предложил:
Если пользователь быстро печатает текст в поле поиска, каждое изменение может вызывать отправку нового запроса на сервер.
| let controller; | ||
| input.addEventListener("input", async (e) => { | ||
| controller?.abort(); | ||
| controller = new AbortController(); | ||
| const res = await fetch(`/search?q=${e.target.value}`, { | ||
| signal: controller.signal, | ||
| }); | ||
| // ... | ||
| }); |
There was a problem hiding this comment.
Не уверен, что это хорошая идея (даже для демонстрации) - выполнять запрос на любое изменение значения без проверки на пустую строку, и трима.
| - **Осторожнее с таймерами.** [`setInterval`](/js/setinterval) мешает интерактивности сильнее, чем разовый [`setTimeout`](/js/settimeout), потому что колбэк срабатывает снова и снова независимо от того, успел ли отработать предыдущий. Проверяйте, не работает ли где-то забытый `setInterval` (частый источник - счётчики, поллинг, автосохранение), и переносите тяжёлую работу из таймеров в Web Worker; | ||
| - **Анимируйте через [CSS](/css/animation), а не `requestAnimationFrame`** там, где это возможно - браузер сможет прогнать анимацию в композиторе, не трогая главный поток. Если JS-анимация всё же нужна, следите, чтобы она не стала non-composited (не анимируйте `width`/`top`/[`box-shadow`](/css/box-shadow), смотрите приёмы для CLS ниже); | ||
| - **Не рендерите большие куски HTML на клиенте через `innerHTML` или аналоги.** В отличие от HTML, который браузер стримит с сервера, у клиентской вставки нет автоматической нарезки на части - вся разметка обрабатывается одной длинной задачей и блокирует кадр. Плюс ресурсы (картинки, скрипты) внутри такой разметки не увидит preload-сканер, это бьёт уже по LCP. Максимизируйте серверный рендеринг (SSR/RSC) и держите то, что всё же дорисовывается на клиенте, компактным; | ||
| - **Грузите меньше JavaScript.** Основные способы собраны ниже, в разделе «[Снижайте размер бандла](#снижайте-размер-бандла)»; |
There was a problem hiding this comment.
Тут я оставлю отметку для редактора, чтобы не забыть про якорные ссылки (возможно потребуется что-то поменять)
|
|
||
| - **Уменьшайте DOM** и применяйте **`content-visibility: auto`**, чтобы браузер не рендерил то, что вне экрана. Lighthouse считает DOM «раздутым» уже от **800 узлов** и предупреждает жёстко после **1400**: чем больше узлов и глубже вложенность, тем дороже каждый пересчёт стилей и layout. Помогают фрагменты вместо оберток (`<>...</>` в React), плоская вёрстка без лишних `div > div > div` и отложенная отрисовка скрытых секций (табы, аккордеоны) до реального обращения к ним. | ||
|
|
||
| `content-visibility: auto` пропускает layout, style и paint для того, что вне экрана, и включает их обратно, как только элемент подъезжает к вьюпорту - на длинной странице (лента, документация с якорями) это заметно ускоряет рендер (в примере Google - с 232 до 30 мс). Обязательно задавайте **`contain-intrinsic-size`**, иначе браузер уменьшит высоту ещё не отрисованного блока до нуля и вы получите те же скачки скролла, которых хотели избежать: |
There was a problem hiding this comment.
Может стоит завернуть описание content-visibility: auto и content-visibility: hidden в <details> / <summary> ?
| #### Если вы используете [React](/tools/react-and-alternatives) | ||
|
|
||
| - Используйте конкурентные возможности - **`useTransition`/`useDeferredValue`**, чтобы пометить тяжёлые обновления как несрочные и не блокировать отклик. Конкурентный рендер сам уступает главный поток примерно **каждые 5 мс**, так что срочные взаимодействия (ввод, клики) не ждут медленного рендера; | ||
| - В SSR-приложениях **дробите гидратацию через `<Suspense>`**: чем гранулярнее границы, тем меньше компонентов React гидратирует синхронно за раз - главный поток блокируется короче. А **серверные компоненты вообще не гидратируются** - для них шаг гидратации пропускается, и это лучшее, что можно сделать для INP в React; |
There was a problem hiding this comment.
| - В SSR-приложениях **дробите гидратацию через `<Suspense>`**: чем гранулярнее границы, тем меньше компонентов React гидратирует синхронно за раз - главный поток блокируется короче. А **серверные компоненты вообще не гидратируются** - для них шаг гидратации пропускается, и это лучшее, что можно сделать для INP в React; | |
| - В SSR-приложениях **дробите гидратацию через `<Suspense>`**: чем гранулярнее границы, тем меньше компонентов React гидратирует синхронно за раз - главный поток блокируется на меньшее время. А **серверные компоненты вообще не гидратируются** - для них шаг гидратации пропускается, и это лучшее, что можно сделать для INP в React; |
|
|
||
| Способы, по сложности усилий: | ||
|
|
||
| - **Сначала измерьте.** Не оптимизируйте вслепую - посмотрите, из чего бандл состоит. `webpack-bundle-analyzer` или `@next/bundle-analyzer` рисуют интерактивную карту, где сразу видно самые большие пакеты (если не уверены, чем вообще [сборщики](/tools/bundlers) отличаются друг от друга и зачем нужны, вроде [Webpack](/tools/webpack) или [Rollup](/tools/rollup) - начните оттуда). Дальше всё просто: измерить → найти самое тяжёлое → заменить или разбить → сжать → поставить бюджет; |
There was a problem hiding this comment.
если не уверены, чем вообще сборщики отличаются друг от друга и зачем нужны, вроде Webpack или Rollup - начните оттуда
Перечитал несколько раз, но не понял. Предлагаю упростить.
|
@vitya-ne может тогда разбить на каждый из параметров? |
Каждая метрика в отдельной статье ? Как по-мне, да, было бы проще читать. |
|
@vitya-ne как лучше организовать - отдельный раздел в веб-платформе или оставить все в "Как устроен веб"? |
Мне кажется, если получится группа статей связанных по смыслу их логичнее объединить в новый подраздел. Предлагаю пока оставить как есть и вернуться к этому вопросу на этапе редакторского ревью. |
Описание
Добавляю статью о web-vitals
Closes #5498
не совсем понятно, куда добавлять в навигации, временно добавил в Веб-платформа->Как устроен веб
Превью: https://content-6101.dev.doka.guide/tools/web-vitals/
Чек-лист
/css/color/,/tools/json/,/tools/gulp/#kak-ponyat)images/example.png,demos/example/,../demos/example/)