Как оптимизировать React приложение с учетом бандлов и JS файлов
Что проверяют
Умеете ли вы искать причину медленной загрузки замером, а не перечислять все техники подряд. Следом спрашивают, как вы нашли самую тяжёлую зависимость и какой эффект получили в цифрах.
Как отвечать
Порядок: замер, правка, повторный замер.
- замер:
rollup-plugin-visualizerдля Vite илиwebpack-bundle-analyzer, вкладка Coverage в Chrome DevTools для неиспользуемого JS, Lighthouse и полевые Core Web Vitals (LCP, INP, CLS). Бюджет размера закрепляют в CI черезsize-limit; - разделение кода:
React.lazyсSuspenseи динамическийimport(), сначала по маршрутам, потом тяжёлые редкие части: редактор, графики, карты, модалки; - tree shaking: работает только на ES-модулях. Проверьте, что библиотеки отдают ESM и объявляют
"sideEffects": false, импортируйте точечно, заменяйте тяжёлое:lodashнаlodash-es,momentнаdate-fnsилиdayjs; - целевые браузеры:
browserslistбез устаревших браузеров убирает лишние полифилы и транспиляцию; - доставка: Brotli на сервере или CDN, хэш в именах файлов с
Cache-Control: max-age=31536000, immutable, отдельный чанк для редко меняющихся зависимостей, чтобы после релиза браузер скачал только ваш код; - приоритеты:
modulepreloadдля критичных чанков,prefetchдля вероятного следующего маршрута.
На Next.js добавьте Server Components: компонент, которому не нужен браузер, в клиентский бандл не попадает вовсе, а next/dynamic делает то же, что React.lazy. Минификацию руками обычно не настраивают: Vite и Next минифицируют продакшн-сборку по умолчанию, а UglifyJS давно вытеснен Terser, esbuild и SWC.
Где спотыкаются
Оптимизируют вслепую. Без отчёта о составе бандла легко потратить день на мелочь, пока одна библиотека весит половину бандла.
Оборачивают в lazy всё подряд, включая первый экран. Получается каскад запросов и спиннеры там, где был готовый контент, и LCP ухудшается.
Путают размер бандла с ререндерами. React.memo и useMemo не уменьшают объём загружаемого JS, это другая задача.