Skip to content

Tree-shaking ​

CDS 4 публикуется как ESM-бандл с сохранённой модульной структурой: каждый компонент — отдельный чанк, рядом с ним лежит его собственный CSS. Благодаря этому сборщик (Vite, webpack, Rollup) может выбросить из приложения всё, что вы не используете.

Чтобы это работало, нужно соблюсти два правила:

  1. Регистрировать в createCds только те компоненты, которые реально нужны.
  2. Подключать базовые стили точечно, а не весь styles.css.

Ниже — как это сделать и сколько это даёт.

Как это устроено ​

  • ESM-бандл собирается с chunkSplitPlugin({ strategy: 'unbundle' }) — модульная структура библиотеки сохраняется, компоненты не склеиваются в один файл.
  • В package.json объявлен sideEffects — сборщик знает, что JS-модули библиотеки чистые и их можно безопасно удалять.
  • При сборке в чанк каждого компонента дописывается импорт его CSS. Поэтому import { CdsButton } тянет за собой ровно CdsButton.css, а не стили всей библиотеки.

Отдельная сборка или плагин для этого не нужны — достаточно обычных именованных импортов.

Точечная регистрация компонентов ​

Передавайте в createCds только используемые компоненты:

ts
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');

Компоненты, зарегистрированные таким образом, доступны в шаблонах глобально — как и при полной регистрации:

html
<template>
  <CdsCard>
    <CdsButton>Нажми меня</CdsButton>
  </CdsCard>
</template>

Важно: createCds не регистрирует компоненты сам. Набор всегда задаёт вызывающая сторона. Это сделано намеренно: если бы фабрика ссылалась на полный список компонентов, сборщик не смог бы удалить ни один из них.

Альтернатива: локальный импорт в SFC ​

Если компонент нужен в одном-двух местах, глобальная регистрация не обязательна — импортируйте его прямо в компоненте:

html
<script setup lang="ts">
import { CdsButton } from '@central-design-system/components';
</script>

<template>
  <CdsButton>Нажми меня</CdsButton>
</template>

createCds всё равно нужен в точке входа — он поставляет темы, локали, иконки и брейкпоинты через provide. Но в components его можно не передавать вовсе.

Полная регистрация ​

Когда набор компонентов заранее неизвестен (внутренние админки, песочницы, документация), проще зарегистрировать всё:

ts
import { createCds, components, directives } from '@central-design-system/components';

const cds = createCds({ components, directives });

Такой вызов отключает tree-shaking компонентов: namespace-импорт components удерживает в бандле всю библиотеку.

Стили ​

Стили компонентов подключаются автоматически вместе с самими компонентами. Вручную нужно подключить только базовый слой — reset, шрифты, порядок CSS-слоёв и глобальные стили элементов:

ts
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 KBCDS-токены для 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) экспортируются именованно и регистрируются так же точечно:

ts
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 не учтён):

СценарийJSJS (gzip)CSSCSS (gzip)
Полная регистрация (components, directives)625 KB203 KB208 KB24.6 KB
createCds + 3 компонента196 KB62.6 KB27.6 KB4.1 KB
createCds + 1 компонент162 KB51.4 KB12.6 KB1.9 KB
1 компонент без createCds63 KB23.4 KB12.6 KB1.9 KB

Цифры зависят от того, какие именно компоненты подключены: составные компоненты (CdsTable, CdsAutocomplete, CdsDatePicker) тянут больше зависимостей, чем атомы.

Что не тришейкается ​

Около 99 KB приходится на ядро, которое createCds подключает всегда: темы, локали, иконки, брейкпоинты (useDisplay), goTo и система defaults. Этот объём не зависит от числа компонентов — он и составляет разницу между строками «1 компонент без createCds» (63 KB) и «createCds + 1 компонент» (162 KB).

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

Как проверить результат ​

bash
npx vite-bundle-visualizer
bash
pnpm dlx vite-bundle-visualizer
bash
yarn dlx vite-bundle-visualizer
bash
bunx vite-bundle-visualizer

На что смотреть:

  • в бандле не должно быть чанков компонентов, которых нет в вашем createCds;
  • если попал «лишний» компонент — скорее всего, он импортируется транзитивно (например, CdsSelect тянет CdsMenu и CdsList), это нормально;
  • если в бандле оказалась вся библиотека — ищите components или import * as среди своих импортов.

Частые ошибки ​

Namespace-импорт вместо именованного. Любой import * as из библиотеки удерживает её целиком:

ts
// ❌ тянет все компоненты
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). Импортируйте набор подпутём:

ts
// ❌ тянет все наборы
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 делают это по умолчанию.