Release·  

Представляем Nuxt Icon v1

Nuxt Icon v1 — современное и настраиваемое решение для иконок в проектах Nuxt.
Anthony Fu

Anthony Fu

@antfu

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

Чтобы закрыть эти задачи, мы сделали Nuxt Icon v1 — универсальное решение для проектов Nuxt. Опираясь на проверенные приёмы и добавляя новые, Nuxt Icon сочетает производительность, удобство и гибкость.

В этой статье разбираем сложности рендеринга иконок, эволюцию решений и то, как Nuxt Icon объединяет лучшие стороны для удобства разработчиков.


Почему иконки — это сложно?

На первый взгляд иконки просты: небольшие изображения, которые дополняют интерфейс и подсказки.

С инженерной точки зрения идеальные иконки должны быть:

  • Окрашиваемыми — под темы и цветовые схемы
  • Масштабируемыми — чёткими в любом размере и разрешении
  • Управляемыми — наборы могут содержать сотни и тысячи иконок
  • Эффективно собранными — минимум сетевых запросов
  • Оптимально загружаемыми — без вреда производительности и UX
  • Динамическими — поддержка подгрузки по запросу или в runtime

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


Эволюция решений для иконок

Со временем разработчики пробовали разные способы рендеринга иконок. Кратко — как развивались эти решения и с какими сложностями сталкивались.

1. Тег <img>: первые шаги

Самый простой вариант — тег <img>. Так делали на заре веба.

Картинки хранятся на сервере, в разметке используется <img> с путём, шириной и высотой. Никакой настройки и рантайм-зависимостей, работает «из коробки» в браузере.

Минусы: пикселизация, сложно менять цвет, плохое масштабирование. Отдельный файл на иконку — много запросов (особенно в эпоху HTTP/1.1). До загрузки возможна «вспышка» пустых иконок. Код получается громоздким из-за путей. Поэтому на современных сайтах этот способ почти не используют.


2. Веб-шрифты: иконочные шрифты

Следующий шаг — веб-шрифты: иконки как символы шрифта. Шрифты векторные и окрашиваемые, поэтому хорошо подходят под иконки.

Провайдеры наборов иконок собирают их в специальный шрифтовой файл, каждой иконке — свой символ Unicode. К нему идёт CSS, который сопоставляет эти символы с классами иконок.

Плюсы очевидны: просто использовать, можно красить и масштабировать, один запрос загружает все иконки.

Минусы: большая загрузка шрифта, сложно кастомизировать набор. До загрузки шрифта возможна «вспышка» невидимых иконок — запасных шрифтов нет.


3. Inline SVG: иконки как компоненты

С появлением современных фреймворков переиспользование HTML-элементов упростилось. Иконки стали вставлять как компоненты — прямо инлайновые SVG.

Многие наборы иконок поставляют обёртки под каждый фреймворк. Например, в MDI один общий компонент и данные иконки в props, в Tabler — отдельный компонент на каждую иконку.

SVG по природе окрашиваемы, масштабируемы и сохраняют все возможности формата. Обычно иконки попадают в бандл приложения — лишних запросов нет, SSR и первая отрисовка работают без проблем.

Минусы: много SVG-элементов в DOM, при большом числе иконок это бьёт по производительности. Растёт размер бандла, для каждой связки «набор + фреймворк» нужна своя интеграция — получается привязка к вендору, сменить набор или фреймворк сложнее.

Несмотря на это, подход распространён: менять набор или фреймворк большинству проектов приходится нечасто.


4. Iconify Runtime: динамический API

Iconify объединил более 200 000 иконок из 100+ коллекций. Runtime-решение подгружает иконки по API — доступ к любой иконке без предварительной сборки.

Удобно для контента от пользователя или любого динамического контента, неизвестного на этапе сборки. Настроить просто, можно использовать даже как CDN без сборки.

Минусы: зависимость от рантайма — иконки появятся только после загрузки JS и данных. SSR и кэширование (например в PWA) усложняются.


5. Иконки-компоненты по требованию

На базе единого API Iconify и подхода Vite «по требованию» мы сделали unplugin-icons: импорт любой иконки как компонента только когда она нужна.

Как unplugin работает с Vite, webpack и rspack. Есть компиляторы для Vue, React, Svelte и Solid. С Iconify одну и ту же иконку можно использовать в любом фреймворке, привязка к вендору минимальна.

Плюсы и минусы те же, что у других компонентных решений; зато сборка даёт доступ ко всей коллекции Iconify, а в бандл попадают только используемые иконки. Вопросы DOM и рантайма при этом остаются.


6. Иконки на чистом CSS

При работе над UnoCSS появилась идея встраивать иконки целиком в CSS — так родилось решение Pure CSS Icons.

SVG подставляются как data URL, для отображения достаточно одного класса. При необходимости иконки можно окрашивать, масштабировать и даже анимировать.

Браузер кэширует CSS, на иконку — один DOM-элемент. Один CSS-файл, без лишних запросов. Чистый CSS — без рантайма, иконки отображаются вместе с интерфейсом и естественно работают с SSR без доработок на сервере.

Минусы: нельзя как угодно кастомизировать внутренние элементы SVG, иконки собираются на этапе сборки — без динамики.


Что нужно для интеграции иконок в Nuxt

Pure CSS Icons и иконки-компоненты по требованию в большинстве случаев достаточно для статики. Nuxt как полноценный фреймворк предъявляет дополнительные требования:

  • SSR/CSR: Nuxt работает и в SSR, и в CSR. Важен опыт пользователя — иконки должны отображаться сразу, без «вспышек».
  • Динамические иконки: в связках вроде Nuxt Content контент может приходить в рантайме или извне и быть неизвестен при сборке. Нужна возможность хорошо интегрироваться с такими сценариями.
  • Производительность: иконки должны эффективно попадать в бандл, загрузка — оптимизирована.
  • Свои иконки: помимо большого выбора в Iconify, у проектов часто есть свои наборы или платные иконки вне Iconify. Поддержка кастомных иконок важна.

С учётом этих требований посмотрим на рассмотренные решения.

Для динамических иконок подходит Iconify Runtime: подгрузка по запросу, контент может быть неизвестен при сборке. Минусы: зависимость от рантайма, неудобная интеграция с SSR, кастомные иконки недоступны — запросы идут на серверы Iconify, у которых нет доступа к локальным наборам.

Pure CSS Icons дают отличную производительность и SSR: иконки сразу в разметке, без мерцания, эффективный бандл. Но для динамических иконок не подходят — нужна сборка, подстройка под контент в рантайме ограничена.

Баланс выдержать сложно. Идея Nuxt Icon v1 — объединить сильные стороны обоих подходов.


Nuxt Icon v1: лучшее из двух подходов

Модульная система Nuxt позволила объединить мгновенный рендеринг CSS-иконок и динамическую подгрузку через Iconify. В итоге — гибкое и современное решение, которое подстраивается под нужды проекта.

Два режима рендеринга

Компонент <Icon> поддерживает режимы CSS и SVG, оба дружественны к SSR. В зависимости от задач можно выбирать режим для каждой иконки.

В режиме CSS иконки попадают в CSS при SSR и отображаются сразу, без затрат в рантайме. В режиме SVG они инлайнятся в HTML при SSR с тем же эффектом. В обоих случаях иконки видны на первом кадре без задержки.


Icon Bundles

Динамические иконки сложнее всего загружать эффективно. Мы используем API Iconify — любая иконка доступна по запросу. Но полагаться только на API значит добавлять задержки, особенно если серверы далеко от пользователя.

Поэтому введены Icon Bundles. Часто используемые иконки можно положить в Client Bundle — они отображаются сразу, без сетевых запросов. Тащить все возможные иконки в клиентский бандл нельзя из-за размера.

Nuxt — full-stack фреймворк, поэтому добавлен Server Bundle. На сервере размер бандла менее критичен, можно включить больше иконок. При SSR они быстро подставляются и уходят на клиент. В итоге частые иконки работают максимально быстро, а при необходимости можно подтянуть любую иконку из Iconify как запасной вариант.

Клиентский бандл для статики и серверный для динамики дают баланс производительности и гибкости.


Поток данных

Как Nuxt Icon запрашивает данные иконок:

  1. Используется компонент <Icon> с указанием name иконки.
  2. Nuxt Icon сначала проверяет Client Bundle и SSR payload (иконки, известные при SSR, уже в payload). Если иконка есть — рендер сразу.
  3. Если на клиенте иконки нет, данные запрашиваются с серверного API вашего Nuxt-приложения. На сервере проверяется Server Bundle.
  4. Задействованы несколько уровней кэша: кэш серверного endpoint, HTTP-кэш и клиентский кэш, чтобы иконки подгружались быстро. Данные иконок меняются редко, поэтому используются жёсткие стратегии кэширования.
  5. Если иконка неизвестна и клиенту, и серверу (динамические иконки), серверный endpoint обращается к API Iconify. Endpoint кэшируется — для каждой уникальной иконки API вызывается один раз независимо от числа клиентов.

Такой многоуровневый подход даёт быструю доставку иконок, баланс скорости и гибкости и по возможности динамику, сглаживая компромиссы между разными решениями.


Попробуйте Nuxt Icon

Nuxt Icon v1 объединяет многолетние наработки в рендеринге иконок. Динамическое приложение, статичный сайт или что-то между — Nuxt Icon подстроится под задачу.

Добавить Nuxt Icon в проект можно одной командой:

npx nuxi module add icon

В компонентах Vue используйте <Icon> с полем name по соглашениям Iconify:

<template>
  <Icon name="i-lucide-activity" />
</template>

Подробнее — в документации. Попробуйте возможности модуля и поделитесь впечатлениями — нам интересно, как Nuxt Icon изменит ваши проекты.

Happy Nuxting ✨

← Вернуться в блог
Nuxt on LinkedInNuxt on BlueskyNuxt on X