Tree-shaking
CDS 4 публикуется как ESM-бандл с сохранённой модульной структурой: каждый компонент — отдельный чанк, рядом с ним лежит его собственный CSS. Благодаря этому сборщик (Vite, webpack, Rollup) может выбросить из приложения всё, что вы не используете.
Чтобы это работало, нужно соблюсти два правила:
- Регистрировать в
createCdsтолько те компоненты, которые реально нужны. - Подключать базовые стили точечно, а не весь
styles.css.
Ниже — как это сделать и сколько это даёт.
Как это устроено
- ESM-бандл собирается с
chunkSplitPlugin({ strategy: 'unbundle' })— модульная структура библиотеки сохраняется, компоненты не склеиваются в один файл. - В
package.jsonобъявленsideEffects— сборщик знает, что JS-модули библиотеки чистые и их можно безопасно удалять. - При сборке в чанк каждого компонента дописывается импорт его CSS. Поэтому
import { CdsButton }тянет за собой ровноCdsButton.css, а не стили всей библиотеки.
Отдельная сборка или плагин для этого не нужны — достаточно обычных именованных импортов.
Точечная регистрация компонентов
Передавайте в createCds только используемые компоненты:
import { createApp } from 'vue';
import '@central-design-system/components/styles/common.css';
import { createCds, CdsButton, CdsCard, CdsIcon } from '@central-design-system/components';
import App from './App.vue';
const cds = createCds({
components: { CdsButton, CdsCard, CdsIcon }
});
createApp(App).use(cds).mount('#app');Компоненты, зарегистрированные таким образом, доступны в шаблонах глобально — как и при полной регистрации:
<template>
<CdsCard>
<CdsButton>Нажми меня</CdsButton>
</CdsCard>
</template>Важно:
createCdsне регистрирует компоненты сам. Набор всегда задаёт вызывающая сторона. Это сделано намеренно: если бы фабрика ссылалась на полный список компонентов, сборщик не смог бы удалить ни один из них.
Альтернатива: локальный импорт в SFC
Если компонент нужен в одном-двух местах, глобальная регистрация не обязательна — импортируйте его прямо в компоненте:
<script setup lang="ts">
import { CdsButton } from '@central-design-system/components';
</script>
<template>
<CdsButton>Нажми меня</CdsButton>
</template>createCds всё равно нужен в точке входа — он поставляет темы, локали, иконки и брейкпоинты через provide. Но в components его можно не передавать вовсе.
Полная регистрация
Когда набор компонентов заранее неизвестен (внутренние админки, песочницы, документация), проще зарегистрировать всё:
import { createCds, components, directives } from '@central-design-system/components';
const cds = createCds({ components, directives });Такой вызов отключает tree-shaking компонентов: namespace-импорт components удерживает в бандле всю библиотеку.
Стили
Стили компонентов подключаются автоматически вместе с самими компонентами. Вручную нужно подключить только базовый слой — reset, шрифты, порядок CSS-слоёв и глобальные стили элементов:
import '@central-design-system/components/styles/common.css';Базовый слой обязателен: именно в нём объявляется порядок @layer, от которого зависит корректный каскад стилей компонентов.
Берите common.css, а не core.css: в core.css нет типографики, поэтому заголовки (h1–h6) останутся с браузерными размерами вместо шкалы CDS.
Доступные варианты:
| Импорт | Размер | Что внутри |
|---|---|---|
styles/common.css | ~9 KB | Рекомендуемая база: core + типографика + grid-утилиты |
styles/core.css | ~2 KB | Только слои, шрифты и reset — без типографики |
styles/tailwind.css | ~12 KB | CDS-токены для Tailwind CSS v4 |
styles/legacy.css | ~253 KB | Совместимость с легаси-разметкой CDS 3 |
Не используйте
@central-design-system/components/stylesв tree-shaking-сценарии. Это агрегат на ~556 KB, содержащий CSS всех компонентов библиотеки. Он удобен для быстрого старта, но полностью обесценивает точечную регистрацию.
Цветовые токены (--cds-color-* и остальные) в CSS-файлах не лежат — их генерирует и вставляет в документ createCds в рантайме, исходя из активной темы. Дополнительно подключать ничего не нужно.
Директивы
Директивы (ClickOutside, Intersect, Touch) экспортируются именованно и регистрируются так же точечно:
import { createCds, ClickOutside } from '@central-design-system/components';
const cds = createCds({
components: { /* ... */ },
directives: { ClickOutside }
});Компонентам библиотеки регистрировать директивы не нужно — те, что используют ClickOutside, Intersect и Touch внутри (CdsOverlay и построенные на нём CdsSelect, CdsMenu, CdsTooltip, CdsSlideView, поля ввода), тянут их собственным импортом и регистрируют локально. Передавайте directives в createCds только ради своей разметки — v-click-outside, v-intersect, v-touch в шаблонах приложения.
В
4.0.0-alpha.0: локальная регистрация внутри компонентов не срабатывала, и менюCdsSelect/CdsMenuне закрывалось по клику вне, если вcreateCdsне передалиdirectives. Если обновиться нельзя — добавьте в регистрациюdirectives: { ClickOutside, Touch }.
Сколько это даёт
Замеры на Vite 5 (production, minify, стили в отдельном файле, Vue включён в бандл, базовый common.css не учтён):
| Сценарий | JS | JS (gzip) | CSS | CSS (gzip) |
|---|---|---|---|---|
Полная регистрация (components, directives) | 625 KB | 203 KB | 208 KB | 24.6 KB |
createCds + 3 компонента | 196 KB | 62.6 KB | 27.6 KB | 4.1 KB |
createCds + 1 компонент | 162 KB | 51.4 KB | 12.6 KB | 1.9 KB |
1 компонент без createCds | 63 KB | 23.4 KB | 12.6 KB | 1.9 KB |
Цифры зависят от того, какие именно компоненты подключены: составные компоненты (CdsTable, CdsAutocomplete, CdsDatePicker) тянут больше зависимостей, чем атомы.
Что не тришейкается
Около 99 KB приходится на ядро, которое createCds подключает всегда: темы, локали, иконки, брейкпоинты (useDisplay), goTo и система defaults. Этот объём не зависит от числа компонентов — он и составляет разницу между строками «1 компонент без createCds» (63 KB) и «createCds + 1 компонент» (162 KB).
Если CDS используется в паре мест и глобальные подсистемы не нужны, можно вообще не вызывать createCds. Но тогда не будет ни тем, ни локалей, ни настроек по умолчанию, поэтому такой режим подходит только для точечных встроек.
Как проверить результат
npx vite-bundle-visualizerpnpm dlx vite-bundle-visualizeryarn dlx vite-bundle-visualizerbunx vite-bundle-visualizerНа что смотреть:
- в бандле не должно быть чанков компонентов, которых нет в вашем
createCds; - если попал «лишний» компонент — скорее всего, он импортируется транзитивно (например,
CdsSelectтянетCdsMenuиCdsList), это нормально; - если в бандле оказалась вся библиотека — ищите
componentsилиimport * asсреди своих импортов.
Частые ошибки
Namespace-импорт вместо именованного. Любой import * as из библиотеки удерживает её целиком:
// ❌ тянет все компоненты
import * as cdsComponents from '@central-design-system/components';
// ✅
import { CdsButton } from '@central-design-system/components';Импорт наборов иконок через iconsets. Объект iconsets ссылается сразу на все наборы, поэтому не тришейкается: в бандл попадёт JS всех паков (включая cds — 46 KB) и, поскольку UnoCSS сканирует их сам, CSS всех пяти наборов (≈370 KB вместо ≈70 KB). Импортируйте набор подпутём:
// ❌ тянет все наборы
import { iconsets } from '@central-design-system/components';
const aliases = iconsets.remix.aliases;
// ✅ только remix
import { remix } from '@central-design-system/components/iconsets/remix';
const aliases = remix.aliases;Подключение агрегата стилей. @central-design-system/components/styles содержит CSS всех компонентов — используйте styles/common.css.
Собственный barrel-реэкспорт. Файл вида src/ui/index.ts, который реэкспортирует десятки компонентов CDS, сводит tree-shaking на нет, если импортировать из него всё подряд. Импортируйте компоненты напрямую из библиотеки.
Сборщик без поддержки ESM. CJS-бандл (dist/cjs) и IIFE-сборка не тришейкаются by design. Убедитесь, что сборщик резолвит import-условие экспорта, — Vite, webpack 5 и Rollup делают это по умолчанию.