Оптимизация загрузки js бандла использующего icon pack’и

fd65038630843361b8cf8651fb4c7f7b.png

Иконки в проекте часто становятся причиной проблем разбухания размера бандла. Все из-за того что svg-иконки могут быть достаточно объемными.

Если мы загружаем только те иконки, что мы реально используем то это не самый плохой вариант. Все становится намного хуже когда мы устанавливаем готовые пакеты иконок, но используем только их малую часть, так произошло и со мной.

На скриншоте видно, что это приложение, состоящее из 2х элементов, весит 722kb. Но почему?

1e4c29ec414d3ff38eb7ade59d35d419.png

Исходные данные

Давайте посмотрим на код, в котором и находится проблема.

d3ea277665a8c0053a353d70aa6eefa5.png

Вроде все просто. У иконки по пропсу name мы понимаем, какую именно иконку из пака нам нужно достать. Но бандл слишком много весит, тут же только одна иконка, почему так? Давайте посмотрим, что происходит внутри компонента иконки.

./components/Icon./components/Icon

Тут видно, что виной раздувания бандла является fontawesome и его наборы пакетов: free-regular-svg-icons, free-brands-svg-icons, free-solid-svg-icons, и т.д.

Мы импортируем сразу все иконки из всех наших пакетов, что и является основной проблемой.

Теперь взглянем на результаты анализа бандла.

537d6d69b498b419a524d0a964d801a3.png

602 kb сжатых gzip данных мы заставляем грузить наших пользователей только для того, чтобы показать им одну иконку! С этим точно нужно что-то сделать.

Сразу оговорюсь, вы можете сказать, что можно было бы загружать только нужные нам иконки через прямые импорты, а потом прогнать из через svg-sprite. Но тогда это привело бы к значительной переработке имеющихся проектов, что было бы крайне нежелательно.

3b630a41d47d13b4730332dbe9ce1a42.png

Мне хотелось бы сохранить интерфейс взаимодействия с компонентом Icon и не заставлять разработчиков каждый раз добавлять новые иконки в проект и писать какой либо дополнительный код. То есть все должно остаться как есть, и при этом наша проблема должна быть решена.

Решение

Для сохранения обратной совместимости внешний интерфейс работы с компонентом Icon не должен меняться. И должна остаться возможность использовать любые иконки из установленных пакетов, а также кастомные иконки. Для достижения этих целей будем использовать динамические импорты. Для каждой иконки из наших пакетов нужно создать отдельную строку в специальных map-файлах.

8cdcab7e465d80ff9f3890b1cf75af48.png

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

В рабочем проекте были куплены расширенные пакеты fontawesome, кол-во иконок в них было примерно 7–8т шт. Писать мамы для такой кучи иконок однозначно задача не одного дня.

Поэтому был написан скрипт который сделает всю рутинную работы за нас.

Результат работы скрипта iconPackMapsGeneratorРезультат работы скрипта iconPackMapsGenerator

Внутри конфиг для его работы.

  • name: название генерируемого файл;

  • lib: путь до папки с файлами иконок;

  • prefix: префикс для названия иконки. Заменяет префикс fa.

514ec0e443d0b099397199a794b69bec.png

Для кастомных иконок создаем отдельную папку, где каждый файл описан следующим образом:

0b21fc6551c5f453d4ff2c10600e56f0.png

И переделываем наш компонент Icon не трогая его интерфейс.

components/Icon.tsxcomponents/Icon.tsx

Вроде все. Что же у нас получилось в итоге?

Давайте вновь посмотрим на бандл.

Как видно, каждая наша иконка помещена в отдельный чанк, и также можно заметить, что размер бандла значительно увеличился. Было 600kb стало 2.01 Mb. Почему так?

Бандлер для создания чанка оборачивает его в специальный код который, как оказалось, весит больше чем многие наши svg.

При ближайшем рассмотрении чанка с компонентом Icon видно что, больше всего места занимают мапы иконок. Но нам нужны эти файлы так как иначе бандлер не поймет что нам нужно создать отдельный чанк под всевозможные иконки. И имея мапу можем создавать ts-типы для названий всех иконок.

aa9a3b3ce7d5ccc85d6634ed8d54ca15.png

А что же происходит теперь в браузере?

7a2c4e9e427748d2d211dadd2698e821.png

Теперь при загрузке страницы мы видим, что последний чанк размером 1.1kb это наша иконка.

При первой загрузки в начальном варианте мы загружали 722kb, а сейчас 89kb! Уже победа. Помимо этого мы получили:

  1. Сохранение обратной совместимости с исходным проектом;

  2. Динамическая загрузка иконок по требованию;

  3. Возможность добавлять кастомные иконки

Заключение

Надеюсь эта статья вам поможет с оптимизацией ваших бандлов и сэкономит вам время и силы. Исходных код используемого тестового проекта вы можете найти по ссылке.

Также если вам интересно в своем Telegram я время от времени выкладываю интересные находки по фронтенду. И всем легких бандлов.

© Habrahabr.ru