JavaScript / производительность
Почему интерфейс тормозит: INP, длинные задачи и JavaScript
Быстрая загрузка не гарантирует отзывчивый сайт. Если клик ждёт длинную задачу JavaScript, пользователь видит задержку даже на странице с хорошим Lighthouse-баллом.
Что измеряет INP
Interaction to Next Paint оценивает отзывчивость на протяжении посещения страницы, а не только при старте. Метрика наблюдает взаимодействия и выбирает одно из наиболее медленных, иногда отбрасывая выбросы. В задержку входят ожидание до начала обработчика, работа обработчиков и время до следующей отрисовки. Поэтому недостаточно измерить только длительность функции click.
Хороший ориентир в документации web.dev — не более 200 мс для 75-го перцентиля посещений, отдельно для мобильных и настольных устройств. Но один итоговый показатель не объясняет причину. Полезные полевые данные должны связывать медленное взаимодействие с конкретным элементом, типом действия и фазой задержки. Без этого команда легко оптимизирует не тот участок.
Как найти виновную работу
Начните с реального действия: пользователь вводит текст в фильтр, нажимает «открыть», переключает вкладку. В Performance trace отметьте момент события и посмотрите, какие задачи выполнялись до обработчика, внутри него и перед paint. Долгая задача может принадлежать стороннему скрипту, сериализации данных, тяжёлому рендеру или вашему циклу по тысячам элементов.
Разделение фаз меняет решение. Если ввод ждёт сторонний скрипт, оптимизация обработчика не поможет. Если обработчик синхронно фильтрует огромный массив и пересоздаёт DOM, нужно уменьшать объём работы. Если вычисления быстры, но layout и paint дороги, смотрите на структуру страницы и стили. Измеряйте на реальном мобильном устройстве или в профиле с ограниченным CPU: мощный ноутбук скрывает проблемы.
Разбивать, переносить, сокращать
Первый шаг — удалить ненужную работу. Не пересчитывайте весь список при каждом вводе, если можно ограничить данные, кешировать неизменные части или виртуализировать длинный список. Если работа необходима, разделите её на части и дайте главному потоку возможность обработать ввод. scheduler.yield() может помочь там, где поддерживается; для остальных сред нужен продуманный fallback.
Вычисления без доступа к DOM иногда разумно вынести в Worker. Но Worker не ускоряет бесплатным образом: данные надо передать, результат вернуть, а DOM всё равно обновлять на главном потоке. Не отправляйте туда маленькую операцию только ради модного API. Если задержку создаёт layout после изменения сотен узлов, лучше уменьшить число изменений и измерить результат.
Проверить результат, а не только график
Повторите то же действие до и после исправления, сравните трассы и затем наблюдайте полевые данные. Лабораторный сценарий полезен для диагностики, но он не отражает все устройства и привычки пользователей. Переход от INP 500 мс к 180 мс в одном демо не гарантирует такого же результата на всех маршрутах.
Не ломайте поведение ради цифры. Отложенная работа должна завершаться предсказуемо, индикатор состояния — соответствовать реальной операции, а фокус и клавиатура — оставаться рабочими. Хорошая оптимизация делает действие ощутимо быстрее и сохраняет смысл интерфейса. Метрика нужна как проверка этого результата, а не как самоцель.
Рамка для принятия решения
В центре решения — снижения INP в JavaScript-интерфейсе: его стоит оценивать в границах «одно медленное взаимодействие и связанные задачи главного потока», а не на абстрактном демо.
Тема снижения INP в JavaScript-интерфейсе легко превращается в спор о вкусах. Чтобы принять инженерное решение, нужно сначала зафиксировать исходное состояние, ожидаемый результат и стоимость ошибки. Полезно сузить пилот до области «одно медленное взаимодействие и связанные задачи главного потока»: так легче сравнить поведение до и после, а также откатить изменение без потери данных.
- Ограничить пилот контуром: одно медленное взаимодействие и связанные задачи главного потока.
- Заранее описать доказательство готовности: полевые данные, профиль производительности и сравнение до/после.
- Выбрать один репрезентативный сценарий: фильтрация большой таблицы после ввода в поле поиска.
- Зафиксировать версии, настройки, права и бюджет, чтобы опыт можно было повторить.
- Назначить человека, который может остановить пилот и оценить качество без авторитета инструмента.
Критерии нужно согласовать до начала: если формулировать их после результата, любой исход можно объявить успехом.
Практический сценарий внедрения
Репрезентативный сценарий для пилота — фильтрация большой таблицы после ввода в поле поиска; он достаточно реален, но остаётся управляемым.
Хороший пилот начинается не с миграции всего проекта, а с одного повторяемого сценария. Для этой темы подойдёт «фильтрация большой таблицы после ввода в поле поиска». Важно записать не только итог, но и путь: какой контекст потребовался, где возникли неясности, сколько было повторов и какие ручные решения остались за разработчиком.
- Снять базовые замеры и сохранить чистую точку отката до начала работы.
- Разбить сценарий на исследование, минимальную реализацию, проверку и независимое ревью.
- Запустить проверки, соответствующие риску: RUM, Performance trace, мобильные устройства и проверку функциональности.
- Собрать артефакты: полевые данные, профиль производительности и сравнение до/после, не ограничиваясь текстом финального отчёта.
- Попросить ревьюера оценить результат по критериям, не зная, какой инструмент или подход использовался.
На каждом шаге должна сохраняться возможность остановиться, понять текущее состояние и продолжить работу вручную. Это делает процесс устойчивым к сбоям и не привязывает команду к одному инструменту.
Риски и защитные механизмы
Особого внимания требует оптимизация лабораторного балла без улучшения реального взаимодействия: эту зону нужно проверять отдельно и не скрывать за общим успехом.
Главный риск здесь — оптимизация лабораторного балла без улучшения реального взаимодействия. Его нельзя снять одним промптом, настройкой или проходящим unit-тестом. Нужна сетка защиты: минимальные права, маленький diff, изолированная среда, автоматические проверки и ручная оценка там, где ошибка влияет на пользователя, данные или производство.
- Отдельно проверить главную зону риска: оптимизация лабораторного балла без улучшения реального взаимодействия.
- Не расширять permissions и доступ к данным только ради более гладкого демо-сценария.
- Не смешивать миграцию, рефакторинг и новую функцию в одном diff: такой патч сложно проверить и откатить.
- Считать непроходящую или неопределённую проверку блокером, а не поводом изменить ожидание.
- Для данных, авторизации, платежей и публичных API оставлять обязательную ручную проверку и явное одобрение.
Защита не должна зависеть от того, что исполнитель «помнит» все инструкции. Критические ограничения лучше выразить правами, CI, валидаторами и средой, которую нельзя обойти обычным текстом.
План внедрения и метрики
Для итогового решения нужны артефакты «полевые данные, профиль производительности и сравнение до/после» и динамика метрик, а не субъективное впечатление от первого запуска.
Пилот стоит признавать успешным только тогда, когда он улучшает измеримую часть работы. Для снижения INP в JavaScript-интерфейсе полезно наблюдать INP p75, длительность обработчика и число долгих задач. Сначала нужна базовая линия без нового подхода, затем две-три недели наблюдения и только после этого — решение о расширении. Единичный удачный пример не заменяет серию повторяемых задач.
- Измерять INP p75, длительность обработчика и число долгих задач на серии задач, а не на лучшем единичном примере.
- Сравнивать медиану и неудачные выбросы: среднее значение часто скрывает дорогие провалы.
- Записывать время человека на постановку, перехваты, ревью и исправления, а не только машинное время.
- Отдельно считать регрессии, откаты, инциденты и доработки, появившиеся уже после слияния.
- По итогам пилота обновить командные инструкции, проверки и ограничения, даже если сам инструмент не будет внедрён.
Хорошее внедрение улучшает не демо, а повторяемый инженерный процесс и качество результата.
После первого цикла стоит пересмотреть не только выбор инструмента, но и сам процесс: какие инструкции оказались неясными, каких проверок не хватило и где ревью началось слишком поздно.
Контрольные вопросы перед внедрением
Перед внедрением нового подхода команде стоит письменно ответить на несколько вопросов. Какую конкретную проблему мы решаем? Как выглядит тот же процесс сегодня? Какие данные, ключи, команды и внешние системы затронуты? Кто имеет право одобрить результат и кто будет разбирать инцидент? Ответы превращают новую технологию из личного эксперимента в управляемое изменение.
- Какой минимальный контур «одно медленное взаимодействие и связанные задачи главного потока» даст полезный ответ без большой миграции?
- Как будет собрано доказательство: полевые данные, профиль производительности и сравнение до/после?
- Нужно ли включить в обязательный набор RUM, Performance trace, мобильные устройства и проверку функциональности?
- По какому сигналу пилот будет остановлен, даже если первые результаты выглядят многообещающе?
Если на эти вопросы нет коротких и проверяемых ответов, то разворачивать новый подход ещё рано. Лучше потратить час на ясные границы, чем неделю на разбор неуправляемого эксперимента.