diff --git a/editorial/agent-rewrites/193.json b/editorial/agent-rewrites/193.json index aa84e9e..17d69bc 100644 --- a/editorial/agent-rewrites/193.json +++ b/editorial/agent-rewrites/193.json @@ -3,5 +3,5 @@ "slug": "editorial-2022-08-field-media-performance", "title": "Когда маленькая картинка тормозит страницу: как найти причину в media-слоте", "excerpt": "Изображение может быть маленьким на экране и большим в сети. Разбираем source mapping, геометрию, priority и безопасную проверку одной обратимой правки.", - "contentHtml": "

Карточка товара занимает большую часть сетевого бюджета, хотя её изображение на экране имеет ширину всего 320 пикселей. Hero появляется поздно. При загрузке фильтра соседний текст прыгает вниз. В отчёте кто-то пишет: «картинка тормозит страницу» — и предлагает добавить priority или заменить файл.

\n

Такой диагноз слишком широк. Ошибка может находиться в исходнике, в расчёте размера слота, в разметке, в CSS или в составе первого экрана. Если поменять CDN-вариант, размеры и атрибуты сразу, команда потеряет связь между причиной и результатом. Цена ошибки — лишний трафик, поздний полезный контент и релиз, который невозможно уверенно откатить.

\n

Тезис: медиа оптимизируют не по размеру файла и не по одному атрибуту. Сначала нужно описать конкретный слот, затем проверить его геометрию и выбор ресурса, после этого сопоставить намерение приложения с фактическим браузерным наблюдением. Одна гипотеза требует одной обратимой правки и повторного прогона в тех же условиях.

\n

Механизм: один слот, несколько независимых решений

\n

Media-слот — это не только URL картинки. У него есть содержимое, ожидаемый размер, вариант кадрирования, плотность экрана, момент появления и место в композиции страницы. Эти решения связаны, но не заменяют друг друга.

\n

Сначала браузер получает HTML и видит элемент img. Атрибуты srcset и sizes дают ему набор кандидатов и ожидаемую ширину слота. Браузер сопоставляет эту подсказку с viewport и плотностью экрана, а затем выбирает ресурс. Если sizes описывает слот как 960 пикселей, хотя фактическая колонка занимает 320, браузер может выбрать слишком крупный кандидат. Малый видимый результат не означает малый сетевой расход.

\n

Геометрия решает другую задачу. Атрибуты width и height задают intrinsic ratio изображения. Когда браузер знает соотношение сторон до загрузки, он может заранее зарезервировать место. Это снижает риск скачка соседнего контента. Но наличие атрибутов не доказывает отсутствие layout shift: контейнер, CSS, шрифт и ветка состояния могут изменить фактический layout.

\n

Priority тоже имеет две стороны. Код может считать hero критичным и отдать ему соответствующее намерение. Это не равно доказанному порядку сетевых запросов или paint. На порядок влияют разметка, другие ресурсы, браузер, кеш и состояние страницы. Поэтому строка конфигурации — гипотеза о намерении, а trace или performance-запись — наблюдение в конкретных условиях.

\n

Пример: описать слот до изменения страницы

\n

Ниже — учебный пример разметки. Он показывает контракт компонента: слот имеет размеры, набор кандидатов и текст для доступности. Пример не открывает страницу, не загружает файл и не измеряет LCP или CLS.

\n
<img\n  src=\"/media/product-640.avif\"\n  srcset=\"/media/product-320.avif 320w,\n          /media/product-640.avif 640w,\n          /media/product-960.avif 960w\"\n  sizes=\"(max-width: 600px) 320px, 640px\"\n  width=\"640\"\n  height=\"480\"\n  alt=\"Чёрный рюкзак на белом фоне\"\n/>
\n

В этом примере sizes должен соответствовать реальной ширине слота, а не желаемому размеру самого большого кандидата. Если карточка на узком экране занимает 320 CSS-пикселей, это нужно проверить через фактический layout. Если мобильная композиция показывает другой фрагмент изображения, одного srcset недостаточно: понадобится art direction через picture и отдельные source.

\n

Нельзя объявлять учебный объект доказательством загрузки. Проверка вроде «в данных есть loaded» говорит только о состоянии модели. Она не подтверждает, что браузер получил response, декодировал изображение и нарисовал его. Для этого нужен отдельный прогон страницы и зафиксированные условия.

\n

Симптомы, причины и действия

\n
Диагностика одного media-слота
СимптомВозможная причинаПроверкаДействие
Маленькая картинка скачивает большой файлНеверный sizes, отсутствует подходящий кандидат или CDN всегда отдаёт desktop-вариантСравнить rendered width, выбранный URL, response size и значения srcset/sizesИсправить mapping или подсказку размера; повторить тот же viewport
Контент прыгает после загрузкиНет известного ratio, контейнер меняет размер или CSS переопределяет рамкуПроверить DOM и computed layout до и после загрузки в одном сценарииЗадать корректные размеры или ratio; проверить фактический layout
Hero появляется поздноРесурс конкурирует с другими запросами, находится далеко в разметке или выбран слишком тяжёлый вариантСнять trace с route, viewport, кешем и браузером; найти request и paint-событияИзменить только один слой: source, композицию или загрузочный путь
Атрибут priority не дал ожидаемого эффектаНамерение приложения приняли за гарантию планировщика браузераРазделить config intent и фактический порядок запросов в traceОставить наблюдаемый результат, проверить конкурирующие ресурсы и не добавлять второй флаг без гипотезы
Изображение стало резким, но объект обрезанПоменяли resolution switching вместо art directionСравнить crop на целевом viewport и описание содержимого в altИспользовать отдельный источник для композиции, сохранив fallback
\n

Иллюстрация границы диагностики

\n
\"Диагностическая
Один симптом может иметь разные причины. Сначала выбирается слой проверки, затем меняется один параметр. Если наблюдение не подтвердило гипотезу, возвращается прежнее значение.
\n

Схема полезна как ограничитель области. Поздний hero нельзя автоматически объяснить отсутствием размеров. Скачок layout нельзя исправить только сменой формата. Большой response нельзя объявить проблемой priority, пока не проверены кандидат и фактическая ширина слота.

\n

Порядок действий

\n
  1. Зафиксируйте симптом. Запишите один URL, один media-слот, viewport, браузер, состояние кеша и наблюдаемое событие. Формулировка «страница медленная» не подходит.
  2. Опишите контракт. Укажите содержимое, src, кандидаты srcset, sizes, размеры, alt, предполагаемый crop и намерение critical или deferred. Называйте намерение именно намерением.
  3. Проверьте данные и разметку. Убедитесь, что ширина и высота положительны, ratio соответствует изображению, fallback существует, а alt описывает изображение, а не имя файла.
  4. Проверьте браузерный слой. Откройте тот же сценарий и сравните фактический rendered width, выбранный ресурс, response, порядок запросов и момент появления. Учебная fixture не заменяет этот шаг.
  5. Выберите одну гипотезу. Например: «на ширине 320 пикселей выбирается кандидат 960w из-за неверного sizes». Не объединяйте её с изменением CSS и priority.
  6. Внесите обратимую правку. Сохраните прежний source mapping или значение атрибута. Не удаляйте evidence: старое наблюдение нужно для сравнения и отката.
  7. Повторите прогон. Используйте те же route, viewport, браузер и cache state. Если гипотеза не подтвердилась, откатите правку и расширьте проверку, а не наслаивайте следующую оптимизацию.
\n

Ограничения и отрицательный путь

\n

Размеры изображения помогают резервировать место, но не исправляют layout, который меняется из-за шрифта, рекламы, пользовательского состояния или позднего CSS. srcset и sizes уменьшают лишний download только при корректном описании слота и доступных кандидатах. picture решает art direction, но добавляет варианты, которые нужно проверить на содержимое и fallback.

\n

Не делайте вывод о LCP по времени ответа картинки. LCP — это наблюдение о крупнейшем отрисованном элементе в конкретной загрузке. Запрос мог завершиться раньше paint, а другой элемент мог стать кандидатом. Актуальная спецификация W3C описывает API и его ограничения, но не гарантирует результат конкретной страницы.

\n

Если trace не подтверждает гипотезу, отрицательный путь имеет конкретный вид: вернуть прежнее значение, сохранить условия прогона, отметить гипотезу как неподтверждённую и выбрать следующий слой. Если картинка всё ещё поздняя после уменьшения ресурса, проверяйте композицию страницы и конкурирующие запросы. Если скачок остаётся после добавления размеров, проверяйте реальный контейнер и CSS. Если source стал меньше, но crop ухудшился, откатите source mapping и решайте задачу art direction.

\n

Проверяемый критерий готовности

\n

Разбор готов, когда для одного слота сохранены четыре вещи: воспроизводимый симптом с условиями; проверенная причина или явно отвергнутая гипотеза; одна правка с понятным откатом; повторный результат в том же сценарии. Для source должны быть видны выбранный URL и размер response. Для геометрии — фактическая рамка до и после загрузки. Для priority — разделённые intent и browser observation.

\n

Не называйте работу завершённой по формулировке «стало быстрее» и не добавляйте выдуманный процент. Учебный HTML подтверждает только структуру примера. Production-вывод требует реальной страницы, реального браузерного прогона и сохранённого артефакта наблюдения. Такой критерий не обещает, что медиа больше никогда не станет проблемой. Он делает следующую ошибку обнаружимой и позволяет откатить спорную правку.

\n

Проверяемые источники

" + "contentHtml": "

Карточка товара занимает большую часть сетевого бюджета, хотя её изображение на экране имеет ширину всего 320 пикселей. Hero появляется поздно. При загрузке фильтра соседний текст прыгает вниз. В отчёте кто-то пишет: «картинка тормозит страницу» — и предлагает добавить priority или заменить файл.

\n

Такой диагноз слишком широк. Ошибка может находиться в исходнике, в расчёте размера слота, в разметке, в CSS или в составе первого экрана. Если поменять CDN-вариант, размеры и атрибуты сразу, команда потеряет связь между причиной и результатом. Цена ошибки — лишний трафик, поздний полезный контент и релиз, который невозможно уверенно откатить.

\n

Тезис: медиа оптимизируют не по размеру файла и не по одному атрибуту. Сначала нужно описать конкретный слот, затем проверить его геометрию и выбор ресурса, после этого сопоставить намерение приложения с фактическим браузерным наблюдением. Одна гипотеза требует одной обратимой правки и повторного прогона в тех же условиях.

\n

Механизм: один слот, несколько независимых решений

\n

Media-слот — это не только URL картинки. У него есть содержимое, ожидаемый размер, вариант кадрирования, плотность экрана, момент появления и место в композиции страницы. Эти решения связаны, но не заменяют друг друга.

\n

Сначала браузер получает HTML и видит элемент img. Атрибуты srcset и sizes дают ему набор кандидатов и ожидаемую ширину слота. Браузер сопоставляет эту подсказку с viewport и плотностью экрана, а затем выбирает ресурс. Если sizes описывает слот как 960 пикселей, хотя фактическая колонка занимает 320, браузер может выбрать слишком крупный кандидат. Малый видимый результат не означает малый сетевой расход.

\n

Геометрия решает другую задачу. Атрибуты width и height задают intrinsic ratio изображения. Когда браузер знает соотношение сторон до загрузки, он может заранее зарезервировать место. Это снижает риск скачка соседнего контента. Но наличие атрибутов не доказывает отсутствие layout shift: контейнер, CSS, шрифт и ветка состояния могут изменить фактический layout.

\n

Приоритет загрузки тоже имеет две стороны. Код или фреймворк может считать hero критичным и передать это намерение загрузочному пути. Это не равно доказанному порядку сетевых запросов или paint. На порядок влияют разметка, другие ресурсы, браузер, кеш и состояние страницы. Поэтому строка конфигурации — гипотеза о намерении, а trace или performance-запись — наблюдение в конкретных условиях.

\n

Пример: описать слот до изменения страницы

\n

Ниже — учебный пример разметки. Он показывает контракт компонента: слот имеет размеры, набор кандидатов и текст для доступности. Пример не открывает страницу, не загружает файл и не измеряет LCP или CLS.

\n
<img\n  src=\"/media/product-640.avif\"\n  srcset=\"/media/product-320.avif 320w,\n          /media/product-640.avif 640w,\n          /media/product-960.avif 960w\"\n  sizes=\"(max-width: 600px) 320px, 640px\"\n  width=\"640\"\n  height=\"480\"\n  alt=\"Чёрный рюкзак на белом фоне\"\n/>
\n

В этом примере sizes должен соответствовать реальной ширине слота, а не желаемому размеру самого большого кандидата. Если карточка на узком экране занимает 320 CSS-пикселей, это нужно проверить через фактический layout. Если мобильная композиция показывает другой фрагмент изображения, одного srcset недостаточно: понадобится art direction через picture и отдельные source.

\n

Нельзя объявлять учебный объект доказательством загрузки. Проверка вроде «в данных есть loaded» говорит только о состоянии модели. Она не подтверждает, что браузер получил response, декодировал изображение и нарисовал его. Для этого нужен отдельный прогон страницы и зафиксированные условия.

\n

Симптомы, причины и действия

\n
Диагностика одного media-слота
СимптомВозможная причинаПроверкаДействие
Маленькая картинка скачивает большой файлНеверный sizes, отсутствует подходящий кандидат или CDN всегда отдаёт desktop-вариантСравнить rendered width, выбранный URL, response size и значения srcset/sizesИсправить mapping или подсказку размера; повторить тот же viewport
Контент прыгает после загрузкиНет известного ratio, контейнер меняет размер или CSS переопределяет рамкуПроверить DOM и computed layout до и после загрузки в одном сценарииЗадать корректные размеры или ratio; проверить фактический layout
Hero появляется поздноРесурс конкурирует с другими запросами, находится далеко в разметке или выбран слишком тяжёлый вариантСнять trace с route, viewport, кешем и браузером; найти request и paint-событияИзменить только один слой: source, композицию или загрузочный путь
Метка priority не дала ожидаемого эффектаНамерение приложения приняли за гарантию планировщика браузераРазделить config intent и фактический порядок запросов в traceОставить наблюдаемый результат, проверить конкурирующие ресурсы и не добавлять второй флаг без гипотезы
Изображение стало резким, но объект обрезанПоменяли resolution switching вместо art directionСравнить crop на целевом viewport и описание содержимого в altИспользовать отдельный источник для композиции, сохранив fallback
\n

Иллюстрация границы диагностики

\n
\"Диагностическая
Один симптом может иметь разные причины. Сначала выбирается слой проверки, затем меняется один параметр. Если наблюдение не подтвердило гипотезу, возвращается прежнее значение.
\n

Схема полезна как ограничитель области. Поздний hero нельзя автоматически объяснить отсутствием размеров. Скачок layout нельзя исправить только сменой формата. Большой response нельзя объявить проблемой priority, пока не проверены кандидат и фактическая ширина слота.

\n

Порядок действий

\n
  1. Зафиксируйте симптом. Запишите один URL, один media-слот, viewport, браузер, состояние кеша и наблюдаемое событие. Формулировка «страница медленная» не подходит.
  2. Опишите контракт. Укажите содержимое, src, кандидаты srcset, sizes, размеры, alt, предполагаемый crop и намерение critical или deferred. Называйте намерение именно намерением.
  3. Проверьте данные и разметку. Убедитесь, что ширина и высота положительны, ratio соответствует изображению, fallback существует, а alt описывает изображение, а не имя файла.
  4. Проверьте браузерный слой. Откройте тот же сценарий и сравните фактический rendered width, выбранный ресурс, response, порядок запросов и момент появления. Учебная fixture не заменяет этот шаг.
  5. Выберите одну гипотезу. Например: «на ширине 320 пикселей выбирается кандидат 960w из-за неверного sizes». Не объединяйте её с изменением CSS и priority.
  6. Внесите обратимую правку. Сохраните прежний source mapping или значение атрибута. Не удаляйте evidence: старое наблюдение нужно для сравнения и отката.
  7. Повторите прогон. Используйте те же route, viewport, браузер и cache state. Если гипотеза не подтвердилась, откатите правку и расширьте проверку, а не наслаивайте следующую оптимизацию.
\n

Ограничения и отрицательный путь

\n

Размеры изображения помогают резервировать место, но не исправляют layout, который меняется из-за шрифта, рекламы, пользовательского состояния или позднего CSS. srcset и sizes уменьшают лишний download только при корректном описании слота и доступных кандидатах. picture решает art direction, но добавляет варианты, которые нужно проверить на содержимое и fallback.

\n

Не делайте вывод о LCP по времени ответа картинки. LCP — это наблюдение о крупнейшем отрисованном элементе в конкретной загрузке. Запрос мог завершиться раньше paint, а другой элемент мог стать кандидатом. Датированный First Public Working Draft W3C от 24 мая 2022 года описывает API и его ограничения, но не гарантирует результат конкретной страницы.

\n

Если trace не подтверждает гипотезу, отрицательный путь имеет конкретный вид: вернуть прежнее значение, сохранить условия прогона, отметить гипотезу как неподтверждённую и выбрать следующий слой. Если картинка всё ещё поздняя после уменьшения ресурса, проверяйте композицию страницы и конкурирующие запросы. Если скачок остаётся после добавления размеров, проверяйте реальный контейнер и CSS. Если source стал меньше, но crop ухудшился, откатите source mapping и решайте задачу art direction.

\n

Проверяемый критерий готовности

\n

Разбор готов, когда для одного слота сохранены четыре вещи: воспроизводимый симптом с условиями; проверенная причина или явно отвергнутая гипотеза; одна правка с понятным откатом; повторный результат в том же сценарии. Для source должны быть видны выбранный URL и размер response. Для геометрии — фактическая рамка до и после загрузки. Для priority — разделённые intent и browser observation.

\n

Не называйте работу завершённой по формулировке «стало быстрее» и не добавляйте выдуманный процент. Учебный HTML подтверждает только структуру примера. Production-вывод требует реальной страницы, реального браузерного прогона и сохранённого артефакта наблюдения. Такой критерий не обещает, что медиа больше никогда не станет проблемой. Он делает следующую ошибку обнаружимой и позволяет откатить спорную правку.

\n

Проверяемые источники

" }