# Что на самом деле попадает в бандл при импорте одного компонента

> Разбор веса одной дизайн-системы — во что обходится компонент, во что вторая тема и почему CSS и JS не коррелируют.

- Page: https://granularity.tech/ru/blog/what-lands-in-your-bundle
- Last reviewed: 2026-09-03

«Это же просто кнопка» — так умирают бюджеты бандла. Честный вопрос не в том,
сколько весит компонент, а в том, **что приезжает вместе с ним**: обвязка
фреймворка, стили всего, что листу велели сгенерировать, вторая тема, которую
никто не просил. Ниже — ответ для одной дизайн-системы, снятый замером, а не
заявленный.

Все числа сняты 2026-08-29 на ядре 0.38.0 и пресете
`@feugene/unocss-preset-granular` 0.15.0. Метод назван рядом с каждым: число без
метода — это утверждение, а не измерение.

С тех пор конвейер CSS сменился: пресет уступил место
[`@feugene/granum`](/ru/docs/installation), и числа уехали вместе с ним. Довод
статьи не уехал — две цены по-прежнему не связаны, — но переносить сами числа не
надо. Актуальные стоят на [главной](/ru) и пересчитываются каждой сборкой, а не
набираются.

## Две цены, и они не связаны

Компонент стоит дважды: в CSS и в JavaScript. Кажется, что тяжёлый компонент
тяжёл в обоих. Это не так.

| Компонент | Сколько добавляет к листу | Собственный код |
|---|---|---|
| `GrCard` | 223 B | 1 870 B |
| `GrDialog` | 318 B | 9 789 B |
| `GrDataTable` | 705 B | 16 243 B |
| `GrSelect` | 2 335 B | 19 819 B |

Сжато gzip, фреймворк объявлен внешним. У `GrDialog` кода в тридцать раз больше,
чем стилей, у `GrSelect` — в восемь. Причина скучная и её стоит знать: стили
растут от числа **видимых состояний**, код — от количества **поведения**. Диалог
это коробка с ловушкой фокуса. Селект — коробка со списком, фильтрацией,
клавиатурной навигацией и плавающей панелью.

Практическое следствие: **вес компонента нельзя оценить, посмотрев на него.**
Самый лёгкий в пакете весит 1 455 B, самый тяжёлый — 19 819 B, разброс в 13.6
раза, и внешняя разница между ними не отличается в 13.6 раза ничем.

## Во что обходится первый компонент

Здесь «это же просто кнопка» действительно неверно — и здесь же действительно
верно.

| Лист | Сжатый |
|---|---|
| Один компонент, одна тема | 7 504 B |
| Один компонент, две темы | 8 250 B |
| Все компоненты ядра, две темы | 17 430 B |

Первый компонент стоит 7 504 B, а оставшиеся 76 — 9 180 B на всех. Это не
парадокс: первый оплачивает фундамент — объявления токенов, базовый слой,
preflight, — а каждый следующий платит только за свои утилиты.

То есть «просто кнопка» неверно про первую кнопку и примерно верно про вторую.
Добавляя дизайн-систему ради одного компонента, вы платите за дизайн-систему.
Добавляя десятый, вы платите за компонент.

## Вторая тема стоит 746 байт

Не 746 килобайт и не второй лист: 746 B, потому что тема — это блок значений
кастомных свойств под селектором, а не копия стилей. Каждое правило с
`var(--gr-bg)` эмитится один раз, сколько бы тем его ни определяло.

Здесь ошибаются в обе стороны. Отказываются от тёмной темы ради веса, которого
она не стоила, и заводят шесть брендовых тем, считая их бесплатными: в CSS они
бесплатны, а в вычитке, проверке контраста и решениях дизайна — нет. Байты здесь
наименьшая из статей расхода.

## Где гранулярные импорты действительно помогают

Пакет объявляет 149 путей экспорта, в ядре 84 компонента. Импорт подпутём, а не
из корневого бареля, — это то, что оставляет остальные 76 снаружи:

```ts
// Отсюда достижим весь барель — tree-shaking обязан доказать,
// что остальное не используется, а через реэкспорты это удаётся не всегда.
import { GrButton } from '@feugene/granularity'

// Один компонент и его граф. Доказывать нечего.
import { GrButton } from '@feugene/granularity/components/GrButton'
```

Работают обе строки. Вторая меньше в любом сборщике и такая же в лучшем — в этом
и весь довод: выигрыш не в том, что она быстрее, когда всё сложилось, а в том,
что она не зависит от того, сложилось ли.

Сторона CSS не встряхивается вовсе — она **порождается**. В листе лежит то, что
конфигу велели сгенерировать, и поэтому сужение `components` — единственное
место, где за гранулярность платят вниманием:

```js
export default defineGranumConfig({
  engine: windEngine(),
  providers: ['@feugene/granularity'],
  components: [{ provider: '@feugene/granularity', names: ['GrButton', 'GrCard'] }],
  themes: { names: ['light', 'dark'] },
})
```

Не написать `components` — получить все: 17 430 B вместо 8 473 B. Это настоящая
разница и плохая настройка, чтобы унаследовать её случайно.

## Чего эти числа не говорят

Три честных ограничения.

**Это сжатые байты, а не время разбора.** 19 819 B `GrSelect` исполняются дольше,
чем 19 819 B разметки, и ни одно из чисел не говорит о третьей отрисовке.

**Фреймворк во всех замерах JS внешний.** Vue в этих числах нет, потому что его
нет в них и у вас — он у вас уже есть. Если нет, прибавьте: тогда дизайн-система
перестанет быть интересной частью бюджета.

**Ноль runtime-зависимостей — свойство ядра, а не семейства.** Ядро объявляет
0 runtime-зависимостей; спутники, оборачивающие чужой редактор или подсветку,
объявляют свои, и они перечислены на странице каждого пакета.

## Одно число, которое стоит запомнить

Не 7 504 и не 13.6 раза. Вот это: первый компонент дизайн-системы стоит примерно
столько же, сколько все остальные вместе. Бюджетируйте решение, а не импорт.
