[Перевод] Atomic CSS здорового человека. UnoCSS
Продолжение перевода статьи »Reimagine Atomic CSS» двухлетней давности одного из членов команды Vue core Anthony Fu, автора UnoCSS, в которой рассматривается уже сам UnoCSS
Часть первая.
Введение в UnoCSS
UnoCSS — мгновенный (instant) атомарный CSS-движок с максимальной производительностью и гибкостью.
Все началось с нескольких случайных экспериментов во время моих национальных праздников. С мыслью о том, что все делается по требованию (on-demand), и гибкостью, которую я ожидаю как пользователь, эксперименты оказались очень полезными для меня во многих отношениях.
Двигатель
UnoCSS — это движок, а не фреймворк, поскольку в нем нет ядерных утилит — все функциональные возможности предоставляются через предустановки или встроенные конфигурации.
Мы представляем себе, что UnoCSS сможет имитировать функциональность большинства существующих атомарных CSS-фреймворков. И, возможно, будет использован в качестве движка для создания новых атомарных CSS-фреймворков! Например:
import UnocssPlugin from '@unocss/vite'
// the following presets do not exist at this moment,
// contribution welcome!
import PresetTachyons from '@unocss/preset-tachyons'
import PresetBootstrap from '@unocss/preset-bootstrap'
import PresetTailwind from '@unocss/preset-tailwind'
import PresetWindi from '@unocss/preset-windi'
import PresetAntfu from '@antfu/oh-my-cool-unocss-preset'
export default {
plugins: [
UnocssPlugin({
presets: [
// PresetTachyons,
PresetBootstrap,
// PresetTailwind,
// PresetWindi,
// PresetAntfu
// pick one... or multiple!
]
})
]
}
Давайте посмотрим, как это стало возможным:
Интуитивный и полностью настраиваемый
Основные цели UnoCSS — интуитивность и настраиваемость. Он позволяет создавать собственные утилиты буквально за считанные секунды.
Далее вы найдете краткое руководство:
Статические правила
Атомарный CSS может оказаться огромным по объему. Важно, чтобы определение правил было простым и легко читаемым. Чтобы создать пользовательское правило для UnoCSS, вы можете написать его следующим образом:
rules: [
['m-1', { margin: '0.25rem' }]
]
Когда m-1
будет обнаружен в кодовой базе пользователя, будет сгенерирован следующий CSS:
.m-1 { margin: 0.25rem; }
Динамические правила
Чтобы сделать его динамическим, измените matcher на RegExp, а body на функцию:
rules: [
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
[/^p-(\d+)$/, match => ({ padding: `${match[1] / 4}rem` })],
]
Первым аргументом функции body является результат совпадения, поэтому вы можете деструктурировать его, чтобы получить группы совпадений RegExp.
Например, при использовании:
Соответствующий CSS будет сгенерирован:
.m-100 { margin: 25rem; }
.m-3 { margin: 0.75rem; }
.p-5 { padding: 1.25rem; }
Вот и все. Осталось только добавить другие утилиты, использующие тот же шаблон, и теперь у вас есть свой собственный атомарный CSS!
Варианты (Variants)
Варианты в UnoCSS также просты и в то же время мощны. Вот несколько примеров:
variants: [
// support `hover:` for all rules
{
match: s => s.startsWith('hover:') ? s.slice(6) : null,
selector: s => `${s}:hover`,
},
// support `!` prefix to make the rule important
{
match: s => s.startsWith('!') ? s.slice(1) : null,
rewrite: (entries) => {
// append ` !important` to all css values
entries.forEach(e => e[1] += ' !important')
return entries
},
}
]
Конфигурации вариантов могут быть немного расширены. Из-за большого объема статьи я опущу подробное объяснение здесь, вы можете обратиться к документации за более подробной информацией.
Пресеты (presets)
Сейчас вы можете упаковать свои пользовательские правила и варианты в пресеты и поделиться ими с другими — или создать даже свой собственный атомарный CSS-фреймворк поверх UnoCSS!
Кроме того, мы поставляем несколько пресетов, чтобы вы могли быстро освоить их.
Однако стоит упомянуть, что стандартный @unocss/preset-uno
пресет (все еще экспериментальный) является общим суперсетом популярных фреймворков, включая Tailwind CSS, Windi CSS, Bootstrap, Tachyons и т.д.
Например, оба ml-3
(Tailwind), ms-2
(Bootstrap), ma4
(Tachyons), mt-10px
(Windi CSS) являются валидными.
.ma4 { margin: 1rem; }
.ml-3 { margin-left: 0.75rem; }
.ms-2 { margin-inline-start: 0.5rem; }
.mt-10px { margin-top: 10px; }
Узнайте больше о предустановке по умолчанию.
Гибкость
До сих пор мы показывали, как можно использовать UnoCSS для имитации поведения Tailwind — хотя мы сделали так, что имитировать Tailwind своими силами очень легко, это само по себе, вероятно, не будет иметь большого значения для пользователей.
Давайте раскроем истинную силу UnoCSS:
Режим атрибутирования (Attributify Mode)
Режим атрибутирования — одна из любимых функций Windi CSS. Он помогает вам лучше организовать и сгруппировать ваши утилиты с помощью атрибутов.
Вот так преобразуется ваш код Tailwind:
к:
Это не только обеспечивает лучшую организацию по категориям, но и избавляет вас от повторного ввода одних и тех же префиксов.
В UnoCSS мы реализовали режим Attributify Mode, используя только один вариант и один экстрактор с менее чем 100 строками кода в общей сложности! Что еще более важно, он напрямую работает для любых пользовательских правил, которые вы определили!
В дополнение к режиму Windi’s Attributify Mode мы также поддерживаем атрибуты без значений с помощью нескольких строк изменений:
теперь можно
Режим атрибутирования предоставляется через пресет @unocss/preset-attributify
, подробное использование смотрите в его документации.
Чистые CSS-иконки
Если вы читали мой предыдущий пост Journey with Icons Continues, то должны знать, что я очень увлечен иконками и активно ищу решения для иконок. На этот раз, благодаря гибкости UnoCSS, у нас даже могут быть чисто CSS-иконки! Да, вы не ослышались, только на CSS и ноль JavaScript! Давайте посмотрим, как это выглядит:
Комбинируя варианты, вы можете даже переключать иконки на основе состояния наведения курсора или даже цветовой схемы. Поиграйте с демонстрацией выше и убедитесь в этом (демонстрация доступна в оригинальной статье — прим. переводчика). Благодаря потрясающему проекту Iconify у вас есть доступ к более чем 10 000 иконок из более чем 100 популярных наборов иконок по требованию.
И снова эта функция написана менее чем на 100 строк кода. Ознакомьтесь с реализацией пресета @unocss/preset-icons
, чтобы узнать, как это работает!
Обновление: Читайте мой новый пост Иконки в чистом CSS, чтобы узнать об этом больше!
Я надеюсь, что эти пресеты дадут вам общее представление о том, насколько гибким является UnoCSS. Учитывая, что он все еще находится на очень ранней стадии, нам еще предстоит исследовать множество возможностей.
Область видимости (scoping)
Еще одна проблема, с которой я столкнулся при использовании Tailwind/Windi, — это префлайт (preflight). Preflight сбрасывает нативные элементы и обеспечивает некоторый откат для переменных CSS. Это замечательно при разработке нового приложения, использующего исключительно Tailwind/Windi, но когда вы хотите заставить их работать с другими фреймворками пользовательского интерфейса или совместно использовать некоторые компоненты с помощью утилит Tailwind, префлайт часто вносит множество конфликтов, которые ломают существующий пользовательский интерфейс.
Поэтому UnoCSS сделал еще один агрессивный шаг, отказавшись от поддержки префлайтов. Вместо этого он оставил контроль над сбросом CSS полностью на усмотрение пользователей (или фреймворков поверх UnoCSS), которые могут использовать то, что соответствует их потребностям (Normalize.css, Reset.css, сброс UI-фреймворков и т. д.)
Это также позволяет UnoCSS иметь больше возможностей для CSS Scoping. Например, у нас есть экспериментальный режим scoped-vue в плагине Vite для генерации scoped-стилей для каждого компонента, чтобы вы могли безопасно поставлять их как библиотеку компонентов, используя атомарный CSS, не беспокоясь о конфликте с CSS пользователей. Например:
Производительность
При всей гибкости и воображении, которые дает UnoCSS, я бы, честно говоря, подумал, что производительность может быть не столь важной вещью, о которой стоит заботиться. Просто из любопытства я написал простой бенчмарк, чтобы сравнить производительность. И, как ни странно, вот результат:
10/21/2021, 2:17:45 PM
1656 utilities | x50 runs
none 8.75 ms / 0.00 ms
unocss v0.0.0 13.72 ms / 4.97 ms (x1.00)
windicss v3.1.9 980.41 ms / 971.66 ms (x195.36)
tailwindcss v3.0.0-alpha.1 1258.54 ms / 1249.79 ms (x251.28)
Оказывается, UnoCSS может быть 200x быстрее, чем JIT Tailwind и Windi CSS. Честно говоря, при подходе «по требованию» и Windi, и Tailwind JIT уже очень быстры, поэтому прирост производительности UnoCSS может быть не очень ощутимым. Однако практически нулевые накладные расходы означают, что вы можете интегрировать UnoCSS в существующий проект для работы с другими фреймворками в качестве дополнительного решения, не беспокоясь о снижении производительности.
Глубоко в UnoCSS было сделано множество оптимизаций производительности. Если вам интересно, вот несколько из них, которые можно взять на заметку:
Нет парсинга, нет AST
Внутренне Tailwind полагался на модификацию AST PostCSS, в то время как Windi написал собственный парсер и AST. Учитывая тот факт, что изменения в утилитах не часто ожидаются во время разработки, UnoCSS генерирует утилиты с помощью очень дешевой конкатенации строк, вместо того чтобы внедрять целый процесс парсера и генерации. При этом UnoCSS агрессивно кэширует имена классов с их сгенерированной CSS-строкой, что позволяет обойти весь процесс подбора и генерации при повторной встрече с теми же утилитами.
Одиночное прохождение
Как уже упоминалось в предыдущем разделе, и Windi CSS, и Tailwind JIT полагаются на предварительное сканирование файловой системы и используют fs watcher для HMR. Файловый ввод-вывод неизбежно влечет за собой некоторые накладные расходы, в то время как ваши инструменты сборки фактически должны загрузить их еще раз. Так почему бы нам не использовать содержимое, которое уже было прочитано инструментами разработки, напрямую?
Помимо независимого ядра генератора, UnoCSS намеренно предоставляет только плагин Vite, что позволяет ему сосредоточиться на наилучшей интеграции с Vite.
Обновление: Теперь он также предоставляет плагин Webpack и время выполнения CSS-in-JS.
В Vite хук transform
будет итерироваться по всем файлам с их содержимым. Поэтому мы можем написать плагин, который будет собирать их, например:
export default {
plugins: [
{
name: 'unocss',
transform(code, id) {
// filter out the files you don't want to scan
if (!filter(id))
return
// scan the code (also handles invalidate on dev)
scan(code, id)
// we just want the content, so we don't transform the code
return null
},
resolveId(id) {
return id === VIRTUAL_CSS_ID ? id : null
},
async load(id) {
// generated css is provide as a virtual module
if (id === VIRTUAL_CSS_ID)
return { code: await generate() }
}
}
]
}
Поскольку Vite также обрабатывает HMR и будет снова задействовать хук transform
при изменении файла, это позволяет UnoCSS завершить все за один проход без дублирования файлового ввода-вывода и fs watcher. Кроме того, при таком подходе сканирование опирается на граф модулей, а не на глобализацию файлов. Это означает, что только те модули, которые были включены в ваше приложение, будут влиять на генерируемый CSS, а не любые файлы в ваших папках.
Есть еще несколько трюков, которые мы использовали, чтобы добиться еще большей производительности. Возможно, позже я напишу о них еще один пост, но до этого вы можете прочитать код, чтобы разобраться :)
Можно ли использовать это сейчас?
Короткий ответ: Да, но с осторожностью.
UnoCSS все еще находится в стадии экспериментов. Но, учитывая его простоту, результат генерации уже достаточно надежен. Одна вещь, о которой вам следует помнить, — API еще не доработаны. Мы действительно будем следовать за semver при выпуске, но, пожалуйста, ожидайте изменений.
Он не предназначен для замены Windi CSS или Tailwind (лучше подождать Windi CSS v4). Мы не рекомендуем полностью переводить существующие проекты на UnoCSS. Вы можете попробовать его в новых проектах или использовать в качестве дополнения к существующему CSS-фреймворку (например, отключить предустановки по умолчанию и использовать предустановку иконок исключительно для иконок с чистым CSS или создать свои собственные правила).
О, кстати, сайт, который вы читаете, теперь исключительно на UnoCSS, чтобы вы могли ссылаться : P.
В то же время, пожалуйста, не стесняйтесь делиться пресетами, которые вы создаете, или помогайте внести свой вклад в наши стандартные пресеты. Нам не терпится увидеть, что вы можете придумать!
Завершение
Большое спасибо за то, что дочитали до конца! Если это вас заинтересовало, не забудьте заглянуть в репозиторий unocss/unocss за подробностями и поиграть с ним в Online Playground.
Anthony Fu
Дополнение (от переводчика)
На данный момент UnoCSS давно уже находится в стабильном состоянии, стал довольно популярен и может быть использован для рабочих проектов. Его сайт — unocss.dev
Чем UnoCSS отличается от Windi CSS?
UnoCSS был создан одним из членов команды Windi CSS, при этом многое было взято из работы, которую мы проделали в Windi CSS.Хотя Windi CSS больше активно не поддерживается (по состоянию на март 2023 года), вы можете рассматривать UnoCSS как «духовного наследника» Windi CSS. UnoCSS унаследовал от Windi характер работы по требованию, режим атрибутирования, ярлыки, группы вариантов, режим компиляции и многое другое.
Кроме того, UnoCSS создан с учетом максимальной расширяемости и производительности, что позволяет нам внедрять такие новые возможности, как чистые CSS-иконки, атрибутирование без значений, tagify, веб-шрифты и т.д. Самое главное, UnoCSS создан как атомарный CSS-движок, где все функции являются необязательными, что позволяет легко создавать собственные конвенции, собственную систему дизайна и собственные пресеты — с теми комбинациями функций, которые вы хотите.
Чем UnoCSS отличается от Tailwind CSS?
И Windi CSS, и UnoCSS черпали вдохновение в Tailwind CSS.Так как UnoCSS создан с нуля, мы можем получить отличный обзор того, как атомарный CSS был разработан с учетом предшествующих искусств и абстрагирован в элегантный и мощный API. Поскольку цели разработки UnoCSS совершенно разные, сравнивать его с Tailwind CSS не совсем корректно. Но мы постараемся перечислить несколько отличий:
Tailwind CSS — это плагин для PostCSS, в то время как UnoCSS — это изоморфный движок с кучей первоклассных интеграций с инструментами сборки (включая плагин для PostCSS).Это означает, что UnoCSS может быть гораздо более гибким для использования в различных местах (например, CDN Runtime, который генерирует CSS на лету) и иметь глубокие интеграции с инструментами сборки для обеспечения лучшего HMR, производительности и опыта разработчика (например, Inspector).
Если не принимать во внимание технические компромиссы, UnoCSS также разработан как полностью расширяемый и настраиваемый, в то время как Tailwind CSS намного менее гибок (opinionated). Создать собственную систему дизайна (или маркеры дизайна) поверх Tailwind CSS может быть сложно, и вы не сможете отойти от условностей Tailwind CSS.С UnoCSS вы можете создавать практически все, что захотите, с полным контролем.
Например, мы реализовали все утилиты, совместимые с Tailwind CSS, в одном пресете, и есть множество замечательных пресетов сообщества, в которых реализованы другие интересные философии.
Благодаря гибкости, которую обеспечивает UnoCSS, мы можем экспериментировать с множеством инновационных функций, например:
«Недостатком» UnoCSS по сравнению с Tailwind CSS можно назвать то, что он не поддерживает систему плагинов и конфигураций Tailwind, что может затруднить миграцию с сильно настроенного проекта Tailwind CSS. Это решение было принято для того, чтобы сделать UnoCSS высокопроизводительным и расширяемым, и мы считаем, что этот компромисс того стоит.
Узнать интересную и полезную информацию о Vue.js и фронтенде в целом, а также изучить фреймворк по переводу лучшего учебника по данной теме »Vue.js 3 — Шаблоны проектирования и Лучшие практики» можно на нашем сайте: Vue-FAQ.org.
Также заходите на наш Телеграм-канал, посвященный Vue.js и фронтенду в целом: https://t.me/vuefaq