Бюджет: почему кажется медленным
У тебя это мгновенно. У тебя быстрый ноутбук, офисный вайфай, тёплый кэш и двенадцать тестовых записей — и ни одно из этого не верно для человека, ради которого ты всё это собрал.
Страница открывается мгновенно. Ты перезагрузил её сегодня четыреста раз.
Теперь та же страница на трёхлетнем среднем Android'е, на мобильном интернете, с холодным кэшем и восемьюстами настоящими записями: девять секунд до появления хоть чего-нибудь и ещё две до того, как касание начнёт что-то делать. Пользователь уже ушёл и багрепорт не заведёт — он просто запомнит твой продукт как медленный.
Твой ноутбук — не мир. Каждое решение о производительности ты принимаешь от имени человека с телефоном похуже и связью похуже.
Два бюджета, и всё умещается в один
Работа над производительностью выглядит сотней несвязанных приёмов ровно до момента, когда замечаешь: тратятся всего два ресурса.
Байты — всё, что должно проехать по сети, прежде чем что-то произойдёт. Скрипты, стили, шрифты, картинки, данные.
Время главного потока — всё, что процессор телефона должен с этими байтами сделать: разобрать, выполнить, разложить, нарисовать. И вот здесь разрыв между твоим ноутбуком и дешёвым телефоном максимален: скачивание может быть медленнее вчетверо, а выполнение JavaScript — вшестеро, потому что мегабайт скрипта — это работа, а не только вес.
Любая оптимизация ниже — это либо «отправить меньше байтов», либо «сделать меньше работы на главном потоке». Понимание, что именно ты тратишь, — бо́льшая часть понимания, что делать.
Что мерить
Три числа описывают то, что человек действительно переживает, и знать их стоит по смыслу, а не по аббревиатурам:
Сколько до появления основного содержимого. Не когда сеть закончила, а когда самая крупная видимая вещь оказалась на экране. Именно это люди имеют в виду под «долго грузится».
Как быстро отзывается на касание. От нажатия до видимой реакции. Эта метрика ловит страницу, которая выглядит загруженной и игнорирует тебя ещё секунду, — а это бесит сильнее медленной загрузки.
Прыгает ли. Содержимое, съезжающее, пока человек читает или тянется к кнопке. Почти всегда это картинки без размеров, вставленные баннеры или подмена шрифта, меняющая размер текста.
Ещё две вещи про измерение. Лабораторные замеры — Lighthouse на твоей машине — годятся для сравнения «до и после»; правдой о твоих пользователях они не являются. Полевые данные, собранные с настоящих сессий, — правда, и обычно она хуже. И итоговый балл не является целью: балл — способ найти то единственное, что реально медленно.
Сначала измерь, потом меняй
Самый частый провал здесь — не незнание, а неверно направленное усилие: человек тратит день на мемоизацию компонентов, пока библиотека дат на 900 килобайт и три несжатых баннера лежат нетронутыми.
Дисциплина — тот же метод отладки, применённый к скорости. Сними профиль. Найди самый крупный блок. Выдвини одну гипотезу о нём. Поменяй одно. Измерь снова. Если число не сдвинулось — откати: оптимизация, которая измеримо не помогает, — это сложность, за которую ты платишь и ничего не получаешь.
Сфера «Как учиться» описывает эту ловушку точно: полировка ключа, который и так открывает. Оптимизировать то, что ты уже умеешь оптимизировать, комфортно и создаёт ощущение работы; настоящая цена почти всегда лежит там, куда ты ещё не заглядывал.
Где обычно время
Примерно в порядке того, как часто каждое оказывается настоящим виновником:
Объём JavaScript. Самая крупная статья расходов в современном фронтенде. Каждый килобайт скачивается, разбирается, компилируется и выполняется — на их телефоне. Дели по маршрутам, чтобы страница везла только своё, подгружай тяжёлые компоненты по требованию и посмотри, что реально лежит в бандле. Почти в каждом проекте есть одна огромная библиотека, подключённая ради одной функции, и её поиск занимает десять минут.
Картинки. Обычно наибольшее количество байтов. Современные форматы драматически меньше старых, картинку нельзя отдавать крупнее, чем она будет показана, srcset позволяет браузеру выбрать, loading="lazy" откладывает то, что ниже первого экрана, а объявленные размеры предотвращают описанные выше прыжки.
Шрифты. Два загруженных шрифта — дизайнерское решение; четыре — уже вопрос производительности. Шрифты задерживают появление текста, поэтому используй font-display: swap, чтобы показать что-то сразу, предзагружай важный и подрезай его до используемых символов. И помни, что системные шрифты существуют и стоят ноль байтов.
Блокирующие отрисовку ресурсы. Стили в шапке задерживают первую отрисовку, а обычные скрипты — разбор. Откладывай то, что не нужно немедленно; это конвейер из первой статьи, использованный намеренно.
Сторонние скрипты. Аналитика, виджеты чата, менеджеры тегов, реклама, встраивания. Самая неисследованная и часто самая крупная одиночная статья: виджет чата может весить больше, чем всё твоё приложение, и работает он на том же потоке, что и твой интерфейс. Каждый из них обязан себя оправдывать, грузиться поздно и быть измеренным. Менеджер тегов стоит проверить особенно: это дверь, через которую любой человек из маркетинга может отправить код твоим пользователям.
Длинные задачи. Всё, что держит главный поток дольше нескольких сотен миллисекунд, делает страницу неотзывчивой. Дроби крупную работу, виртуализируй длинные списки, чтобы рисовать сорок строк вместо четырёх тысяч, не чередуй чтение и запись DOM и анимируй только transform и opacity.
Кэширование. Имена файлов с отпечатком плюс долгое время жизни кэша означают, что вернувшийся посетитель почти ничего не качает. CDN кладёт эти файлы рядом с пользователем. И то и другое — конфигурация, а не код, и оба огромны по эффекту.
Ощущаемая скорость — настоящая скорость
У пользователя нет секундомера. У него есть ощущение, отзывается ли вещь, и здесь можно выиграть много, ничего не ускоряя.
Отвечай на любое взаимодействие немедленно — примерно за одну десятую секунды реакция ощущается мгновенной и вызванной им. Даже если результат займёт две секунды, кнопка обязана подтвердить нажатие сейчас.
Показывай форму раньше данных. Скелет в раскладке будущего содержимого читается быстрее крутилки и намного быстрее пустой области.
Будь оптимистичным там, где можно. Покажи сообщение отправленным, товар добавленным, лайк засчитанным — а потом сверься с сервером. Правило: делай так только там, где сбой редок и обратим, и честно обрабатывай сбой, когда он случается.
Расставляй приоритет по видимому. Грузи верх страницы первым и откладывай всё, что ниже сгиба. Никто не замечает работу, сделанную для части экрана, до которой не долистали.
Проверяй как пользователь
Три привычки, все дешёвые:
Ограничивай. Инструменты разработчика умеют изображать медленную сеть и вчетверо более слабый процессор. Поработай так один день в неделю — и заметишь то, что иначе невидимо.
Возьми настоящий дешёвый телефон. Эмуляция не воспроизводит ни нагрев реального устройства, ни нехватку памяти, ни его браузер. Один средний Android на полке — лучший инструмент производительности, который может быть у команды.
Проверяй на реальных объёмах данных. Двенадцать строк ведут себя совершенно не так, как восемьсот. Большинство катастроф с отрисовкой невидимы, пока данные ненастоящие, — поэтому они и доезжают до прода в целости.
На практике
Сделай одно измерение до любой оптимизации. Каждый раз. Без базовой линии не отличить улучшение от шевеления.
Открой разбивку бандла на этой неделе. Первый взгляд обычно находит что-нибудь абсурдное.
Проверь сторонние скрипты: выпиши все внешние, назови, зачем каждый, и один удали или отложи.
Проставь размеры всем картинкам и переведи те, что ниже сгиба, на ленивую загрузку. Два мелких изменения, чинящих прыжки и кусок байтового бюджета.
Виртуализируй самый длинный список в проекте или сделай постраничным. Рисовать тысячи строк — решение, а не необходимость.
Держи число «до и после» для всего, что меняешь. Это разница между работой над производительностью и театром производительности — и то же правило, что «не чини баг, который не можешь объяснить».
Проверь себя
Закрой статью и ответь своими словами:
- Каковы два бюджета и почему разрыв по процессору на дешёвых телефонах больше, чем по сети?
- Назови три вещи, которые стоит мерить, обычными словами.
- Почему лабораторный балл — не правда о твоих пользователях?
- Какая дисциплина не даёт оптимизировать не то?
- Почему объём JavaScript дороже такого же веса картинок?
- Назови три способа сделать быстрее на ощущение, не ускоряя ничего.
- Почему проверка на двенадцати записях прячет большинство проблем отрисовки?
Коротко
- Всё тратит один из двух бюджетов: байты по сети или время главного потока, — и по дешёвым телефонам сильнее бьёт второе.
- Мерь то, что чувствуют люди: когда появляется основное содержимое, как быстро отзывается касание и прыгает ли страница.
- Лабораторные числа сравнивают «до и после», полевые данные — правда, а балл нужен, чтобы найти настоящую проблему.
- Измерь, поменяй одно, измерь снова — и откатывай оптимизации, не сдвинувшие число.
- Обычные виновники по порядку: объём JavaScript, картинки, шрифты, блокирующие ресурсы, сторонние скрипты, длинные задачи, отсутствующее кэширование.
- Сторонние скрипты — самая неисследованная и часто самая крупная одиночная статья расходов.
- Ощущаемая скорость считается: подтверждай взаимодействие мгновенно, показывай скелеты, будь оптимистичным там, где сбой редок, и расставляй приоритет по видимому.
- Разрабатывай с ограничениями, проверяй на настоящем дешёвом телефоне с реальными объёмами данных и держи число «до и после» для каждого изменения.