Как оптимизировать большой скрипт, если он тормозит?
Что проверяют
Начинаете ли вы с измерения, а не с догадок, и понимаете ли, как работает event loop. «Тормозит» может означать долгую загрузку или подвисание интерфейса, и это разные задачи. Следом часто спрашивают, чем макрозадача отличается от микрозадачи и почему Promise не спасает от фризов.
Как отвечать
Сначала профилирование. Вкладка Performance в DevTools с замедлением CPU показывает долгие задачи (long tasks, дольше 50 мс) и функции, которые съедают время. Вкладка Coverage покажет, какая часть загруженного кода вообще не выполняется. У пользователей такие задачи ухудшают INP.
Дальше по найденной причине:
- алгоритм:
findвнутриmap, поиск по массиву вместоMapилиSet. Переход с O(n²) на O(n) даёт больше любых микрооптимизаций; - DOM: чередование чтения размеров и записи стилей вызывает принудительный layout на каждой итерации. Сначала все чтения, потом все записи, изменения через
requestAnimationFrame, длинные списки через виртуализацию; - дробление работы: большой цикл режется на порции, между ними управление возвращается браузеру через
setTimeout,scheduler.yield()там, где он поддерживается, илиrequestIdleCallbackдля фоновой работы; - Web Worker: тяжёлые вычисления уходят в отдельный поток. DOM из воркера недоступен, данные идут через
postMessage, крупныйArrayBufferпередаётся без копирования как transferable; - загрузка:
deferдля скриптов, разделение бандла иimport()для кода, который нужен не сразу, удаление лишних зависимостей; - события:
debounceиthrottleнаscroll,input,resize, пассивные слушатели.
Где спотыкаются
Режут цикл через Promise.resolve().then() или queueMicrotask. Очередь микрозадач выполняется целиком до отрисовки, и интерфейс продолжает висеть.
Переписывают код без замера и оптимизируют то, что занимает 2% времени.
Уносят в воркер код, который трогает DOM, или гоняют через postMessage огромные объекты на каждый кадр. Структурное копирование само становится узким местом.