本章概述
本章将深入探讨 UnoCSS 的核心机制:预设系统和规则引擎。我们将学习如何使用内置预设、理解规则引擎的工作原理,以及如何创建自定义预设和规则。
预设系统概述
什么是预设
预设(Preset)是 UnoCSS 的核心概念,它是一组预定义的规则、变体、快捷方式和配置的集合。预设使得 UnoCSS 具有高度的模块化和可扩展性。
// 预设的基本结构
const myPreset = {
name: 'my-preset',
rules: [
// 规则定义
],
variants: [
// 变体定义
],
shortcuts: {
// 快捷方式定义
},
theme: {
// 主题配置
},
preflights: [
// 预检样式
]
}
预设的优势
- 模块化:将相关功能组织在一起
- 可复用:可以在多个项目中使用
- 可组合:多个预设可以组合使用
- 可扩展:可以基于现有预设进行扩展
- 可维护:便于版本管理和更新
内置预设详解
1. @unocss/preset-uno
这是 UnoCSS 的默认预设,提供了最常用的工具类。
// 使用默认预设
import { defineConfig, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
],
})
包含的功能: - 基础工具类(margin、padding、color 等) - Flexbox 和 Grid 布局 - 响应式断点 - 状态变体(hover、focus 等) - 基础动画和过渡
示例用法:
<!-- 基础样式 -->
<div class="p-4 m-2 bg-blue-500 text-white rounded-lg">
基础样式
</div>
<!-- 布局 -->
<div class="flex items-center justify-center h-screen">
<div class="grid grid-cols-3 gap-4">
<div class="col-span-2">内容</div>
<div>侧边栏</div>
</div>
</div>
<!-- 响应式 -->
<div class="w-full md:w-1/2 lg:w-1/3">
响应式宽度
</div>
<!-- 状态变体 -->
<button class="bg-blue-500 hover:bg-blue-600 focus:ring-2 focus:ring-blue-300">
交互按钮
</button>
2. @unocss/preset-wind
提供与 Tailwind CSS 兼容的工具类。
import { defineConfig, presetWind } from 'unocss'
export default defineConfig({
presets: [
presetWind(),
],
})
特点: - 完全兼容 Tailwind CSS 语法 - 包含所有 Tailwind CSS 的工具类 - 支持 Tailwind CSS 的配置格式 - 便于从 Tailwind CSS 迁移
配置示例:
import { defineConfig, presetWind } from 'unocss'
export default defineConfig({
presets: [
presetWind({
// Tailwind CSS 兼容配置
dark: 'class', // 或 'media'
attributify: true,
}),
],
// Tailwind CSS 风格的主题配置
theme: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
}
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
extend: {
spacing: {
'72': '18rem',
'84': '21rem',
'96': '24rem',
}
}
}
})
3. @unocss/preset-mini
最小化的预设,只包含最基础的功能。
import { defineConfig, presetMini } from 'unocss'
export default defineConfig({
presets: [
presetMini(),
],
})
适用场景: - 需要最小化 CSS 输出 - 自定义程度要求很高的项目 - 性能要求极高的场景
4. @unocss/preset-icons
图标预设,支持数万个图标。
import { defineConfig, presetIcons } from 'unocss'
export default defineConfig({
presets: [
presetIcons({
collections: {
// 加载图标集
carbon: () => import('@iconify-json/carbon/icons.json').then(i => i.default),
mdi: () => import('@iconify-json/mdi/icons.json').then(i => i.default),
tabler: () => import('@iconify-json/tabler/icons.json').then(i => i.default),
},
// 自定义图标
customizations: {
iconCustomizer(collection, icon, props) {
// 自定义图标处理
}
},
// 额外属性
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
}
}),
],
})
使用示例:
<!-- 基础图标 -->
<div class="i-carbon-user text-2xl"></div>
<div class="i-mdi-heart text-red-500"></div>
<div class="i-tabler-star text-yellow-500"></div>
<!-- 图标按钮 -->
<button class="flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded">
<div class="i-carbon-add"></div>
添加项目
</button>
<!-- 图标列表 -->
<ul class="space-y-2">
<li class="flex items-center gap-2">
<div class="i-carbon-checkmark text-green-500"></div>
已完成任务
</li>
<li class="flex items-center gap-2">
<div class="i-carbon-time text-yellow-500"></div>
进行中任务
</li>
</ul>
5. @unocss/preset-typography
排版预设,提供美观的文本排版样式。
import { defineConfig, presetTypography } from 'unocss'
export default defineConfig({
presets: [
presetTypography({
// 自定义 CSS 选择器前缀
cssExtend: {
'code': {
color: '#8b5cf6',
},
'a:hover': {
color: '#f59e0b',
},
'a:visited': {
color: '#8b5cf6',
},
}
}),
],
})
使用示例:
<!-- 基础排版 -->
<article class="prose">
<h1>文章标题</h1>
<p>这是一段正文内容,包含了 <a href="#">链接</a> 和 <code>代码</code>。</p>
<blockquote>
这是一个引用块。
</blockquote>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
</ul>
</article>
<!-- 不同尺寸 -->
<div class="prose prose-sm">小号排版</div>
<div class="prose prose-lg">大号排版</div>
<div class="prose prose-xl">超大号排版</div>
<!-- 深色模式 -->
<div class="prose dark:prose-invert">
深色模式排版
</div>
6. @unocss/preset-web-fonts
Web 字体预设,简化字体加载和使用。
import { defineConfig, presetWebFonts } from 'unocss'
export default defineConfig({
presets: [
presetWebFonts({
// Google Fonts
provider: 'google',
fonts: {
sans: 'Roboto',
mono: ['Fira Code', 'Fira Mono:400,700'],
lobster: 'Lobster',
custom: {
name: 'My Custom Font',
src: 'url("/fonts/custom.woff2")',
}
},
// 字体显示策略
display: 'swap',
// 预加载
preload: true,
// 内联 CSS
inlineImports: true,
}),
],
})
使用示例:
<!-- 使用预设字体 -->
<h1 class="font-lobster text-4xl">装饰性标题</h1>
<p class="font-sans">正文内容</p>
<code class="font-mono">代码内容</code>
<!-- 字体权重 -->
<p class="font-sans font-light">细体文本</p>
<p class="font-sans font-bold">粗体文本</p>
7. @unocss/preset-attributify
属性化预设,允许使用 HTML 属性来应用样式。
import { defineConfig, presetAttributify } from 'unocss'
export default defineConfig({
presets: [
presetAttributify({
// 配置选项
prefix: 'un-',
prefixedOnly: false,
nonValuedAttribute: true,
}),
],
})
使用示例:
<!-- 传统方式 -->
<div class="bg-blue-500 text-white p-4 rounded-lg hover:bg-blue-600">
传统方式
</div>
<!-- 属性化方式 -->
<div
bg="blue-500 hover:blue-600"
text="white"
p="4"
rounded="lg"
>
属性化方式
</div>
<!-- 布尔属性 -->
<div flex items-center justify-center>
布尔属性
</div>
<!-- 带前缀 -->
<div
un-bg="red-500"
un-text="white"
un-p="4"
>
带前缀的属性
</div>
规则引擎详解
规则的类型
1. 静态规则
// 静态规则示例
const staticRules = [
// 简单的键值对
['center', { 'text-align': 'center' }],
['red', { color: 'red' }],
// 多个样式属性
['btn', {
'padding': '0.5rem 1rem',
'border-radius': '0.25rem',
'font-weight': '500',
}],
// 使用 CSS 变量
['primary', {
'color': 'var(--primary-color)',
'background-color': 'var(--primary-bg)',
}],
]
2. 动态规则
// 动态规则示例
const dynamicRules = [
// 数字匹配
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
[/^p-(\d+)$/, ([, d]) => ({ padding: `${d / 4}rem` })],
// 颜色匹配
[/^text-(.+)$/, ([, color]) => ({ color })],
[/^bg-(.+)$/, ([, color]) => ({ 'background-color': color })],
// 复杂匹配
[/^grid-cols-(\d+)$/, ([, cols]) => ({
'grid-template-columns': `repeat(${cols}, minmax(0, 1fr))`,
})],
// 条件匹配
[/^w-(\d+)\/(\d+)$/, ([, numerator, denominator]) => {
const percentage = (parseInt(numerator) / parseInt(denominator)) * 100
return { width: `${percentage}%` }
}],
]
3. 函数式规则
// 函数式规则示例
const functionRules = [
// 访问主题配置
[/^text-(.+)$/, ([, color], { theme }) => {
const colorValue = theme.colors?.[color]
if (colorValue) {
return { color: colorValue }
}
}],
// 复杂逻辑处理
[/^shadow-(.+)$/, ([, type]) => {
const shadows = {
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1)',
}
return shadows[type] ? { 'box-shadow': shadows[type] } : undefined
}],
// 生成多个样式
[/^gradient-(.+)-to-(.+)$/, ([, from, to]) => {
return {
'background-image': `linear-gradient(to right, ${from}, ${to})`,
'background-clip': 'text',
'-webkit-background-clip': 'text',
'color': 'transparent',
}
}],
]
规则匹配机制
// 规则匹配流程
class RuleEngine {
constructor(rules) {
this.rules = rules
this.cache = new Map()
}
match(className) {
// 检查缓存
if (this.cache.has(className)) {
return this.cache.get(className)
}
// 遍历规则
for (const rule of this.rules) {
const [pattern, handler] = rule
// 静态规则匹配
if (typeof pattern === 'string') {
if (pattern === className) {
const result = { css: handler, matched: true }
this.cache.set(className, result)
return result
}
}
// 动态规则匹配
if (pattern instanceof RegExp) {
const match = className.match(pattern)
if (match) {
const css = typeof handler === 'function'
? handler(match, { theme: this.theme })
: handler
if (css) {
const result = { css, matched: true }
this.cache.set(className, result)
return result
}
}
}
}
// 未匹配
const result = { matched: false }
this.cache.set(className, result)
return result
}
}
规则优先级
// 规则优先级配置
export default defineConfig({
rules: [
// 高优先级规则(后定义的优先级更高)
['important-rule', { color: 'red !important' }],
// 普通规则
[/^text-(.+)$/, ([, color]) => ({ color })],
// 低优先级规则
['text-default', { color: 'black' }],
],
// 层级配置
layers: {
'base': -1,
'components': 0,
'utilities': 1,
'overrides': 2,
},
})
创建自定义预设
基础预设结构
// my-preset.js
export function createMyPreset(options = {}) {
return {
name: 'my-preset',
// 规则定义
rules: [
// 自定义规则
['my-center', {
'display': 'flex',
'align-items': 'center',
'justify-content': 'center',
}],
// 动态规则
[/^my-space-(\d+)$/, ([, d]) => ({
'margin': `${d * 0.25}rem`,
'padding': `${d * 0.25}rem`,
})],
],
// 变体定义
variants: [
// 自定义变体
(matcher) => {
if (!matcher.startsWith('my-hover:')) return matcher
return {
matcher: matcher.slice(9),
selector: s => `${s}:hover`,
}
},
],
// 快捷方式
shortcuts: {
'my-btn': 'px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600',
'my-card': 'p-6 bg-white rounded-lg shadow-md border',
},
// 主题配置
theme: {
colors: {
'my-primary': '#3b82f6',
'my-secondary': '#64748b',
},
spacing: {
'my-xs': '0.5rem',
'my-sm': '1rem',
'my-md': '1.5rem',
'my-lg': '2rem',
},
},
// 预检样式
preflights: [
{
getCSS: ({ theme }) => `
.my-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 ${theme.spacing?.['my-sm'] || '1rem'};
}
`
}
],
// 选项处理
options,
}
}
高级预设功能
// advanced-preset.js
import { createGenerator } from 'unocss'
export function createAdvancedPreset(options = {}) {
const {
prefix = '',
colors = {},
utilities = true,
components = true,
} = options
return {
name: 'advanced-preset',
// 条件规则
rules: [
// 仅在启用 utilities 时添加
...(utilities ? [
[new RegExp(`^${prefix}u-(\\w+)$`), ([, utility]) => {
// 处理工具类
return generateUtility(utility)
}],
] : []),
// 仅在启用 components 时添加
...(components ? [
[new RegExp(`^${prefix}c-(\\w+)$`), ([, component]) => {
// 处理组件类
return generateComponent(component)
}],
] : []),
],
// 动态主题
theme: {
colors: {
// 合并用户颜色
...getDefaultColors(),
...colors,
},
// 动态断点
breakpoints: generateBreakpoints(options.breakpoints),
},
// 后处理器
postprocess: (util) => {
// 添加前缀
if (prefix && util.selector) {
util.selector = util.selector.replace(/\./g, `.${prefix}`)
}
return util
},
// 扩展其他预设
extend: options.extend,
}
}
// 辅助函数
function generateUtility(utility) {
const utilities = {
'reset': {
'margin': '0',
'padding': '0',
'box-sizing': 'border-box',
},
'clearfix': {
'&::after': {
'content': '""',
'display': 'table',
'clear': 'both',
}
},
}
return utilities[utility]
}
function generateComponent(component) {
const components = {
'button': {
'display': 'inline-flex',
'align-items': 'center',
'justify-content': 'center',
'padding': '0.5rem 1rem',
'border': 'none',
'border-radius': '0.375rem',
'font-weight': '500',
'cursor': 'pointer',
'transition': 'all 0.2s',
},
'card': {
'background': 'white',
'border-radius': '0.5rem',
'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.1)',
'padding': '1.5rem',
},
}
return components[component]
}
预设组合和扩展
// preset-composition.js
import { presetUno, presetWind } from 'unocss'
import { createMyPreset } from './my-preset'
import { createAdvancedPreset } from './advanced-preset'
// 组合多个预设
export function createCompositePreset(options = {}) {
return {
name: 'composite-preset',
// 继承其他预设
presets: [
presetUno(),
createMyPreset(options.myPreset),
createAdvancedPreset(options.advanced),
],
// 额外的规则
rules: [
// 覆盖或扩展规则
['composite-special', {
'background': 'linear-gradient(45deg, #ff6b6b, #4ecdc4)',
'color': 'white',
'padding': '1rem',
'border-radius': '0.5rem',
}],
],
// 合并主题
theme: {
extend: {
colors: {
'composite': {
50: '#f0f9ff',
500: '#0ea5e9',
900: '#0c4a6e',
}
}
}
},
}
}
实际应用示例
企业级预设
// enterprise-preset.js
export function createEnterprisePreset(brandConfig) {
const { colors, fonts, spacing, components } = brandConfig
return {
name: 'enterprise-preset',
rules: [
// 品牌颜色
...Object.entries(colors).map(([name, value]) => [
`brand-${name}`,
{ color: value }
]),
// 组件规则
['enterprise-header', {
'background': colors.primary,
'color': colors.onPrimary,
'padding': spacing.lg,
'box-shadow': '0 2px 4px rgba(0,0,0,0.1)',
}],
['enterprise-sidebar', {
'background': colors.surface,
'border-right': `1px solid ${colors.outline}`,
'width': '240px',
'height': '100vh',
}],
],
shortcuts: {
// 企业级组件快捷方式
'enterprise-btn-primary': `px-6 py-3 rounded font-medium transition-colors`,
'enterprise-card': `bg-white rounded-lg shadow border p-6`,
'enterprise-form-field': `w-full px-3 py-2 border rounded focus:outline-none focus:ring-2`,
},
theme: {
colors,
fontFamily: {
brand: fonts.primary,
mono: fonts.mono,
},
spacing,
},
preflights: [
{
getCSS: () => `
:root {
--enterprise-primary: ${colors.primary};
--enterprise-secondary: ${colors.secondary};
--enterprise-surface: ${colors.surface};
}
.enterprise-layout {
font-family: ${fonts.primary};
color: ${colors.onSurface};
background: ${colors.background};
}
`
}
],
}
}
// 使用企业级预设
const brandConfig = {
colors: {
primary: '#1976d2',
secondary: '#dc004e',
surface: '#ffffff',
background: '#f5f5f5',
onPrimary: '#ffffff',
onSurface: '#212121',
outline: '#e0e0e0',
},
fonts: {
primary: ['Roboto', 'sans-serif'],
mono: ['Roboto Mono', 'monospace'],
},
spacing: {
xs: '0.5rem',
sm: '1rem',
md: '1.5rem',
lg: '2rem',
xl: '3rem',
},
}
export default defineConfig({
presets: [
createEnterprisePreset(brandConfig),
],
})
响应式预设
// responsive-preset.js
export function createResponsivePreset(breakpoints = {}) {
const defaultBreakpoints = {
xs: '320px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
...breakpoints,
}
return {
name: 'responsive-preset',
rules: [
// 容器规则
['container', {
'width': '100%',
'margin-left': 'auto',
'margin-right': 'auto',
'padding-left': '1rem',
'padding-right': '1rem',
}],
// 响应式显示
[/^(block|inline|flex|grid|hidden)-(xs|sm|md|lg|xl|2xl)$/, ([, display, bp]) => {
const mediaQuery = `@media (min-width: ${defaultBreakpoints[bp]})`
return {
[mediaQuery]: {
display: display === 'hidden' ? 'none' : display,
}
}
}],
],
variants: [
// 响应式变体
...Object.entries(defaultBreakpoints).map(([name, size]) => {
return (matcher) => {
if (!matcher.startsWith(`${name}:`)) return matcher
return {
matcher: matcher.slice(name.length + 1),
parent: `@media (min-width: ${size})`,
}
}
}),
],
theme: {
screens: defaultBreakpoints,
},
preflights: [
{
getCSS: () => {
// 生成容器响应式样式
const containerStyles = Object.entries(defaultBreakpoints)
.map(([name, size]) => {
const maxWidth = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
}[name]
if (maxWidth) {
return `
@media (min-width: ${size}) {
.container {
max-width: ${maxWidth};
}
}
`
}
return ''
})
.join('')
return containerStyles
}
}
],
}
}
性能优化
规则缓存
// 规则缓存优化
class OptimizedRuleEngine {
constructor(rules) {
this.rules = rules
this.cache = new Map()
this.staticRules = new Map()
this.dynamicRules = []
// 预处理规则
this.preprocessRules()
}
preprocessRules() {
for (const rule of this.rules) {
const [pattern, handler] = rule
if (typeof pattern === 'string') {
// 静态规则直接存储
this.staticRules.set(pattern, handler)
} else {
// 动态规则存储到数组
this.dynamicRules.push(rule)
}
}
}
match(className) {
// 检查缓存
if (this.cache.has(className)) {
return this.cache.get(className)
}
// 优先检查静态规则
if (this.staticRules.has(className)) {
const result = {
css: this.staticRules.get(className),
matched: true
}
this.cache.set(className, result)
return result
}
// 检查动态规则
for (const [pattern, handler] of this.dynamicRules) {
const match = className.match(pattern)
if (match) {
const css = typeof handler === 'function'
? handler(match, { theme: this.theme })
: handler
if (css) {
const result = { css, matched: true }
this.cache.set(className, result)
return result
}
}
}
// 未匹配
const result = { matched: false }
this.cache.set(className, result)
return result
}
}
懒加载预设
// 懒加载预设
export function createLazyPreset() {
return {
name: 'lazy-preset',
// 懒加载规则
rules: [
// 基础规则立即加载
['flex', { display: 'flex' }],
['block', { display: 'block' }],
// 复杂规则懒加载
[/^complex-(.+)$/, async ([, type]) => {
const { generateComplexRule } = await import('./complex-rules')
return generateComplexRule(type)
}],
],
// 懒加载主题
theme: {
get colors() {
return import('./theme-colors').then(m => m.colors)
},
get spacing() {
return import('./theme-spacing').then(m => m.spacing)
},
},
}
}
本章总结
通过本章学习,我们深入了解了:
- 预设系统:UnoCSS 的模块化核心机制
- 内置预设:各种官方预设的功能和使用方法
- 规则引擎:静态、动态和函数式规则的工作原理
- 自定义预设:如何创建和组合自定义预设
- 性能优化:规则缓存和懒加载等优化技巧
核心要点
- 预设是 UnoCSS 功能的载体,提供了高度的模块化
- 规则引擎支持多种类型的规则,满足不同需求
- 自定义预设可以封装项目特定的样式逻辑
- 性能优化对于大型项目至关重要
下一步
在下一章中,我们将学习原子化 CSS 的具体应用和各种工具类的使用方法。
练习题
- 预设创建:创建一个包含你常用样式的自定义预设
- 规则编写:编写一个动态规则来处理渐变背景
- 预设组合:组合多个预设并解决可能的冲突
- 性能测试:测试不同规则类型的性能差异
- 企业应用:为一个企业项目设计完整的预设系统