← Назад к вопросам
Как сборщик узнает какие CSS классы используются?
2.0 Middle🔥 201 комментариев
#HTML и CSS#Оптимизация и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как сборщик узнает какие CSS классы используются
Это вопрос о том, как инструменты вроде Tailwind CSS и другие CSS-in-JS решения определяют, какие стили нужны в финальном бандле. Понимание этого критично для оптимизации размера CSS файлов.
Static Analysis (Статический анализ)
Сборщик анализирует исходный код без его выполнения, ища CSS классы:
// Tailwind CSS использует регулярные выражения для поиска классов
// В конфиге tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}', // Пути для сканирования
'./public/index.html'
]
};
// Процесс:
// 1. Читает все файлы из указанных путей
// 2. Ищет классы в виде строк, например: class="px-4 py-2"
// 3. Создает CSS только для найденных классов
// 4. Удаляет неиспользуемые классы (tree-shaking)
Как Tailwind находит классы
// РАБОТАЕТ: статические строки
function Button() {
return <button className="px-4 py-2 bg-blue-500">Click</button>;
}
// РАБОТАЕТ: шаблонные строки с переменными
const size = 'lg';
function Input() {
return <input className={`input input-${size}`} />;
}
// НЕ РАБОТАЕТ: динамические классы
const buttonClasses = ['px-4', 'py-2']; // Массив теряется
function Button() {
return <button className={buttonClasses.join(' ')}>Click</button>;
}
// НЕ РАБОТАЕТ: условное объединение
function Badge({ active }) {
const classStr = active ? 'bg-green' : 'bg-gray';
return <span className={classStr}>Status</span>;
}
// РАБОТАЕТ: использовать библиотеку clsx или cn()
import { cn } from '@/lib/utils';
function Badge({ active }) {
return (
<span className={cn(
'px-2 py-1',
active ? 'bg-green-500' : 'bg-gray-300'
)}>
Status
</span>
);
}
Регулярные выражения для поиска
// Tailwind использует примерно такой regex для поиска классов
const classNameRegex = /[^<>]*(?:(?:<(?!\/|\!)[^>]*>|[^<>]+)*</;[^<>]*)?/g;
// Или проще: ищет паттерны вроде:
// - class="..."
// - className="..."
// - :class="..."
// - @class('...')
// - класс-1234 (UUID или числа)
// - неполные классы: px- не проходит, только px-4
const testCases = [
'className="px-4 py-2 bg-blue-500"', // Найдет все три
'class={`text-lg ${condition && "font-bold"}`', // Найдет статические
'className={dynamicClass}', // НЕ найдет (переменная)
'data-class="hidden"', // Может найтись или нет
];
Как работает PurgeCSS и инструменты оптимизации
// В tailwind.config.js или postcss.config.js
module.exports = {
content: [
'src/**/*.{js,jsx,ts,tsx}',
'public/**/*.html'
],
css: ['./src/styles/globals.css'],
safelist: [
'text-red-500', // Гарантировать включение
'bg-blue-\\[\\d+\\]', // Regex для динамических
],
blocklist: [
'hidden', // Исключить из бандла
]
};
// Процесс оптимизации:
// 1. Сканирует все файлы content
// 2. Извлекает CSS классы
// 3. Генерирует CSS только для найденных
// 4. Добавляет safelist (гарантированные)
// 5. Исключает blocklist
// 6. Результат: маленький CSS файл вместо 3-5 MB
Проблемы с динамическими классами
// ПРОБЛЕМА 1: Классы в переменных
const colors = {
primary: 'text-blue-500',
danger: 'text-red-500'
};
function Alert({ type }) {
// Tailwind НЕ найдет text-blue-500 и text-red-500
return <div className={colors[type]}>Message</div>;
}
// РЕШЕНИЕ: используй статические значения
function Alert({ type }) {
return (
<div className={cn(
type === 'primary' && 'text-blue-500',
type === 'danger' && 'text-red-500'
)}>
Message
</div>
);
}
// ПРОБЛЕМА 2: Арифметика с классами
const spacing = 'p-' + (Math.random() * 10); // 'p-3.14...'
// РЕШЕНИЕ: используй существующие значения
const spacingOptions = ['p-1', 'p-2', 'p-3', 'p-4'];
const spacing = spacingOptions[Math.floor(Math.random() * 4)];
Способ 1: Class Name Composition
// lib/utils.ts - используется в проекте PrepBro
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Компонент
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
}
const baseStyles = 'font-medium rounded-lg transition-colors';
const variantStyles = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-300 text-black hover:bg-gray-400'
};
const sizeStyles = {
sm: 'px-2 py-1 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
};
export function Button({
variant = 'primary',
size = 'md',
className,
...props
}: ButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return (
<button
className={cn(
baseStyles,
variantStyles[variant],
sizeStyles[size],
className
)}
{...props}
/>
);
}
// Все классы СТАТИЧЕСКИЕ - Tailwind их найдет
Способ 2: CSS-in-JS (Styled Components)
// styled-components анализирует по-другому
import styled from 'styled-components';
// Инструмент парсит JS код и ищет шаблонные строки
const Button = styled.button`
padding: 8px 16px;
background-color: ${props => props.primary ? 'blue' : 'gray'};
border-radius: 4px;
&:hover {
opacity: 0.8;
}
`;
// Стили генерируются в runtime, не требуют static analysis
Способ 3: CSS Modules
// styles.module.css
.button {
padding: 8px 16px;
background-color: blue;
border-radius: 4px;
}
.button:hover {
opacity: 0.8;
}
// Button.jsx
import styles from './styles.module.css';
export function Button() {
return <button className={styles.button}>Click</button>;
}
// Сборщик знает о классе через import, не нужен static analysis
Способ 4: Safelist для сложных случаев
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{js,jsx}'],
safelist: [
// Гарантировать включение этих классов
'text-red-500',
'text-green-500',
'text-blue-500',
// Или с regex
{
pattern: /text-(red|green|blue)-(500|600|700)/,
},
{
pattern: /bg-(red|green|blue)-(100|200|300)/,
},
]
};
Реальный пример из PrepBro
// components/ui/badge.tsx
import { cn } from '@/lib/utils';
interface BadgeProps {
variant?: 'default' | 'success' | 'warning' | 'error';
size?: 'sm' | 'md';
children: React.ReactNode;
}
const variantClasses = {
default: 'bg-surface-secondary text-content-primary',
success: 'bg-green-100 text-green-800',
warning: 'bg-yellow-100 text-yellow-800',
error: 'bg-red-100 text-red-800'
};
const sizeClasses = {
sm: 'px-2 py-1 text-xs',
md: 'px-3 py-1.5 text-sm'
};
export function Badge({
variant = 'default',
size = 'sm',
children
}: BadgeProps) {
return (
<span
className={cn(
'inline-flex items-center rounded-full font-medium',
variantClasses[variant],
sizeClasses[size]
)}
>
{children}
</span>
);
}
Инструменты для анализа
# Проверить какие классы будут включены
npm run build
# Или использовать Tailwind CLI
npx tailwindcss -i input.css -o output.css --content 'src/**/*.{js,jsx}'
# Проверить размер CSS
ls -lh dist/styles.css
# Analyzer для webpack
npm install --save-dev webpack-bundle-analyzer
Выводы
- Static Analysis — инструменты сканируют исходный код без выполнения
- Regex patterns — ищут строковые классы вроде class="..."
- Динамические классы НЕ работают — не используй array join
- Используй cn() функцию — для безопасного объединения классов
- Safelist для сложных — когда нельзя избежать динамики
- CSS-in-JS — иначе работает, стили в runtime
- CSS Modules — сборщик знает классы через import
- Тестируй бандл — проверяй что CSS не раздувается