Производительность

Гранулярность — главное обещание продукта. Страница показывает, чем оно измеряется и как проверить его самому.

Обещание формулируется коротко: бандл растёт от того, что вы используете, а не от того, что есть в пакете. Ниже — чем это обеспечено, сколько стоит на самом деле и как померить у себя, не веря на слово.

Две стороны, и они не коррелируют

Вес компонента складывается из кода и стилей, и эти величины живут отдельно:

КомпонентCSS, прирост листаJS, сам компонент
GrCard223 B1 870 B
GrDialog318 B9 789 B
GrDataTable705 B16 243 B
GrSelect2 335 B19 819 B

Тяжёлый по коду компонент может почти ничего не добавить к листу, и наоборот. Поэтому «вес компонента» одним числом не измеряется, а любая таблица с одной колонкой врёт.

Код: подпуть вместо бареля

ts
// один компонент и его граф
import { GrButton } from '@feugene/granularity/components/GrButton'

// весь пакет
import { GrButton } from '@feugene/granularity'

Верхняя строка — это и есть гранулярность. Разброс между самым лёгким и самым тяжёлым подпутём — почти два порядка: GrButtonGroup тянет 1.8 kB gzip, меньше процента от бареля, GrDialogService — 89.3 kB. Весь пакет из корня — 537.9 kB.

Складывать веса подпутей нельзя. Общий чанк посчитан в каждой строке заново, а платится один раз: сумма пяти самых тяжёлых строк даёт 382.3 kB, а вместе они весят 171.7 kB. Набор считается объединением, а не суммой — и это верхняя граница: бандлер приложения трясёт дерево дальше и минифицирует повторно.

Полная таблица по каждому подпути генерируется из собранного dist на каждом релизе — она лежит в пакете и на странице каждого компонента в каталоге.

Стили: платите за выбранное

CSS не импортируется файлами — его генерирует пресет ровно для тех компонентов, которые вы перечислили в components. Структура расхода такая:

  1. Первый компонент несёт фундамент — токены, базовый слой, preflight, тему. Это разовая плата.
  2. Каждый следующий добавляет свои сотни байт — от двух сотен у карточки до пары килобайт у селекта.
  3. Вторая тема стоит около килобайта, а не вдвое: темы отличаются значениями ролей, а не правилами.

Не перечислять components вовсе — рабочий вариант: тогда в лист приедут все компоненты провайдера. Разница между «всё» и «один» на общем листе — примерно вдвое, а не в сто раз, потому что фундамент один на всех. Гранулярность стилей имеет смысл, но её эффект скромнее, чем у кода, и честно сказать об этом дешевле, чем потом объясняться.

Рантайм

  • Ноль runtime-зависимостей у ядра. Единственная обязательная внешняя библиотека — @floating-ui/dom, и она объявлена peer-зависимостью приложения, чтобы не приехать второй копией.
  • CSS компонента едет в его же чанке. Отдельного стилевого импорта нет — и это не удобство, а условие: иначе гранулярность ломалась бы на первом же забытом импорте.
  • Подсветка, справочник токенов и тест-помощники — отдельные подпути, и в основной бандл они не попадают вовсе.

Как померить у себя

  1. Соберите приложение с одним компонентом и снимите gzip эмитированного CSS и JS.
  2. Добавьте второй компонент и снимите снова. Разница — его настоящая цена в вашем проекте.
  3. Сравните с барелем: замените подпуть на импорт из корня и повторите. Разница — то, за что вы платите за удобство.

Числа на этом сайте сняты ровно так же. Смотреть на них стоит как на порядок величины: ваш результат зависит от версии бандлера, настроек минификации и от того, что ещё лежит в графе.

Что не ускорит ничего

  • importStyle: true у резолвера авто-импорта. Ядру он не нужен: у большинства компонентов своего CSS нет, а у остальных он уже в чанке.
  • Ручные импорты styles/*.css поверх пресета. Они добавят вес, а не уберут: то же самое пресет уже сгенерировал.
  • Сужение themes.names до одной темы ради экономии. Вторая тема стоит около килобайта — это не то место, где стоит терять переключение.

Последняя ревизия: 2026-09-01