Мышление «сначала утилиты» — Примеры
Пример 1 — Один и тот же список классов в четырёх местах
Список классов панели инструментов скопирован в четыре шаблона, так что теперь для изменения отступа нужны четыре правки, — а одну из них в прошлый раз пропустили. Оцените, чего стоит повторение, и скажите, где место этой абстракции, — а затем исправьте.
<div class="flex items-center gap-2 rounded-md border px-3 py-1.5">
<span class="text-sm font-medium">Заказы</span>
</div>
<!-- те же двенадцать классов повторены ещё в трёх шаблонах -->
Решение
export function Toolbar({ children }) {
return (
<div className="flex items-center gap-2 rounded-md border px-3 py-1.5">
{children}
</div>
);
}
<Toolbar><span className="text-sm font-medium">Заказы</span></Toolbar>
<Toolbar><span className="text-sm font-medium">Счета</span></Toolbar>
Когда список утилит повторяется, абстракция здесь — компонент, а не новый семантический класс. Изобретая класс, вы возвращаетесь к проблемам именования и удаления, от которых ушли, но инструментов у вас теперь меньше. Компонент хранит список в одном файле, носит имя, которое можно произнести вслух, и каждое место вызова передаёт только отличающееся. Утилиты делают оформление читаемым там, где оно применяется, — и как раз граница компонента не даёт этой читаемости превратиться в шаблон, который никто не станет проверять. Выделять стоит на втором повторении, а не на десятом.
Пример 2 — Имена классов, собранные из строк
Помощник выбирает размер текста, склеивая префикс с переменной, и в разработке всё работает, а в собранном выводе стилей нет. Объясните, почему сборка их не видит, и напишите классы так, чтобы они выжили.
function Label({ size = "sm", children }) {
return <span className={"text-" + size}>{children}</span>; // text-sm так и не сгенерируется
}
Решение
const sizeClass = { sm: "text-sm", md: "text-base", lg: "text-lg" };
function Label({ size = "sm", children }) {
return <span className={sizeClass[size]}>{children}</span>;
}
Сборка обходит исходники в поисках буквальных имён классов и генерирует только найденные правила, поэтому имя, собранное во время выполнения, ничему не соответствует и правило не создаётся. Отсюда и разница: в разработке стиль работает, ведь там ничего не фильтруется, а в продакшене пропадает. Если держать каждый возможный класс буквально в объекте-словаре, сканер найдёт три настоящих имени, а место вызова не изменится. Альтернатива — список исключений в конфигурации — работает, но незаметно растёт, и следующий компонент, который склеивает имя из строк, наследует тот же невидимый отказ.