本章概述
本章将探讨 UnoCSS 的高级特性,包括属性化模式、图标系统、Web字体集成、排版预设等,以及如何开发和使用插件来扩展 UnoCSS 的功能。
属性化模式 (Attributify Mode)
什么是属性化模式
属性化模式允许你将工具类写在HTML属性中,而不是class属性中,这样可以提高代码的可读性和组织性。
<!-- 传统方式 -->
<div class="bg-blue-500 text-white p-4 rounded-lg shadow-md hover:bg-blue-600 transition-colors">
传统方式
</div>
<!-- 属性化模式 -->
<div
bg="blue-500 hover:blue-600"
text="white"
p="4"
rounded="lg"
shadow="md"
transition="colors"
>
属性化模式
</div>
启用属性化模式
// uno.config.js
import { defineConfig, presetAttributify, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetAttributify({
// 配置选项
prefix: 'un-', // 属性前缀
prefixedOnly: false, // 是否只使用带前缀的属性
nonValuedAttribute: true, // 支持无值属性
ignoreAttributes: ['class', 'style'], // 忽略的属性
}),
],
})
属性化语法详解
1. 基础语法
<!-- 颜色属性 -->
<div bg="red-500" text="white">红色背景,白色文字</div>
<div bg="gradient-to-r from-blue-500 to-purple-600">渐变背景</div>
<!-- 尺寸属性 -->
<div w="64" h="32">固定尺寸</div>
<div w="full" h="screen">全宽全高</div>
<div w="1/2" h="auto">响应式尺寸</div>
<!-- 间距属性 -->
<div p="4" m="2">内外边距</div>
<div px="6" py="3">水平垂直边距</div>
<div pt="2" pb="4" pl="3" pr="5">单独设置</div>
<!-- 布局属性 -->
<div flex="~ col items-center justify-between">弹性布局</div>
<div grid="~ cols-3 gap-4">网格布局</div>
<div absolute="~ top-0 left-0">绝对定位</div>
2. 响应式属性
<!-- 响应式属性 -->
<div
w="full sm:1/2 md:1/3 lg:1/4"
p="2 sm:4 md:6 lg:8"
text="sm sm:base md:lg lg:xl"
>
响应式属性
</div>
<!-- 复杂响应式布局 -->
<div
flex="~ col sm:row"
items="center sm:start"
justify="center sm:between"
gap="4 sm:6 md:8"
>
<div flex="1">内容1</div>
<div flex="1">内容2</div>
</div>
3. 伪类属性
<!-- 交互状态 -->
<button
bg="blue-500 hover:blue-600 active:blue-700"
text="white"
p="2 4"
rounded="md"
transition="colors"
>
交互按钮
</button>
<!-- 焦点状态 -->
<input
border="2 gray-300 focus:blue-500"
rounded="md"
p="2"
outline="none"
ring="0 focus:2 focus:blue-500"
>
<!-- 组合状态 -->
<div
group
bg="white hover:gray-50"
border="1 gray-200"
rounded="lg"
p="4"
cursor="pointer"
>
<h3 text="lg font-semibold group-hover:text-blue-600">标题</h3>
<p text="gray-600 group-hover:text-gray-800">描述</p>
</div>
4. 无值属性
<!-- 无值属性 -->
<div flex items-center justify-center>居中布局</div>
<div hidden>隐藏元素</div>
<div relative>相对定位</div>
<div absolute>绝对定位</div>
<div fixed>固定定位</div>
<!-- 带前缀的无值属性 -->
<div un-flex un-items-center un-justify-center>带前缀</div>
属性化模式最佳实践
1. 组织属性
<!-- 推荐:按功能分组 -->
<div
<!-- 布局 -->
flex="~ col items-center justify-center"
<!-- 尺寸 -->
w="full max-w-md"
h="auto min-h-64"
<!-- 间距 -->
p="6"
m="4"
<!-- 外观 -->
bg="white"
border="1 gray-200"
rounded="lg"
shadow="md"
<!-- 交互 -->
hover="shadow-lg"
transition="shadow"
>
内容
</div>
<!-- 避免:混乱的属性顺序 -->
<div bg="white" flex p="6" w="full" rounded="lg" items-center>
内容
</div>
2. 复杂布局示例
<!-- 卡片组件 -->
<article
bg="white dark:gray-800"
border="1 gray-200 dark:gray-700"
rounded="xl"
shadow="sm hover:md"
transition="shadow"
overflow="hidden"
>
<!-- 图片区域 -->
<div
w="full"
h="48"
bg="gray-200"
overflow="hidden"
>
<img
w="full"
h="full"
object="cover"
src="image.jpg"
alt="图片"
>
</div>
<!-- 内容区域 -->
<div p="6">
<h3
text="xl font-semibold gray-900 dark:white"
mb="2"
>
文章标题
</h3>
<p
text="gray-600 dark:gray-300"
leading="relaxed"
mb="4"
>
文章摘要内容...
</p>
<div
flex="~ items-center justify-between"
>
<span
text="sm gray-500 dark:gray-400"
>
2024年1月15日
</span>
<button
bg="blue-500 hover:blue-600"
text="white"
px="4"
py="2"
rounded="md"
text="sm font-medium"
transition="colors"
>
阅读更多
</button>
</div>
</div>
</article>
<!-- 表单组件 -->
<form
space="y-6"
max-w="md"
mx="auto"
p="6"
bg="white"
rounded="lg"
shadow="md"
>
<div>
<label
block
text="sm font-medium gray-700"
mb="2"
>
邮箱地址
</label>
<input
type="email"
w="full"
px="3"
py="2"
border="1 gray-300 focus:blue-500"
rounded="md"
outline="none"
ring="0 focus:2 focus:blue-500"
placeholder="请输入邮箱"
>
</div>
<div>
<label
block
text="sm font-medium gray-700"
mb="2"
>
密码
</label>
<input
type="password"
w="full"
px="3"
py="2"
border="1 gray-300 focus:blue-500"
rounded="md"
outline="none"
ring="0 focus:2 focus:blue-500"
placeholder="请输入密码"
>
</div>
<button
type="submit"
w="full"
bg="blue-500 hover:blue-600"
text="white"
py="2"
px="4"
rounded="md"
font="medium"
transition="colors"
>
登录
</button>
</form>
图标系统
图标预设介绍
UnoCSS 提供了强大的图标系统,支持多种图标库的集成。
// uno.config.js
import { defineConfig, presetIcons, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetIcons({
// 图标集合
collections: {
// 使用 Iconify 图标
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),
// 自定义图标
custom: {
logo: '<svg>...</svg>',
icon1: '<svg>...</svg>',
},
},
// 配置选项
scale: 1.2, // 默认缩放
warn: true, // 警告缺失图标
prefix: 'i-', // 图标前缀
extraProperties: { // 额外CSS属性
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
],
})
图标使用方法
1. 基础用法
<!-- 基础图标 -->
<div class="i-carbon-home"></div>
<div class="i-mdi-account"></div>
<div class="i-tabler-settings"></div>
<!-- 自定义图标 -->
<div class="i-custom-logo"></div>
<!-- 图标尺寸 -->
<div class="i-carbon-home text-lg"></div>
<div class="i-carbon-home text-2xl"></div>
<div class="i-carbon-home w-8 h-8"></div>
<!-- 图标颜色 -->
<div class="i-carbon-home text-red-500"></div>
<div class="i-carbon-home text-blue-600"></div>
2. 响应式图标
<!-- 响应式尺寸 -->
<div class="i-carbon-menu text-lg sm:text-xl md:text-2xl"></div>
<!-- 响应式显示 -->
<div class="i-carbon-menu block md:hidden"></div>
<div class="i-carbon-close hidden md:block"></div>
3. 图标组合
<!-- 带文字的图标 -->
<button class="flex items-center space-x-2">
<div class="i-carbon-add"></div>
<span>添加</span>
</button>
<!-- 图标按钮 -->
<button class="p-2 rounded-md hover:bg-gray-100">
<div class="i-carbon-settings w-5 h-5"></div>
</button>
<!-- 图标列表 -->
<ul class="space-y-2">
<li class="flex items-center space-x-3">
<div class="i-carbon-checkmark text-green-500"></div>
<span>已完成</span>
</li>
<li class="flex items-center space-x-3">
<div class="i-carbon-time text-yellow-500"></div>
<span>进行中</span>
</li>
<li class="flex items-center space-x-3">
<div class="i-carbon-close text-red-500"></div>
<span>已取消</span>
</li>
</ul>
自定义图标集
1. SVG图标集
// 自定义图标集
const customIcons = {
// 简单SVG
'arrow-right': '<svg viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" fill="currentColor"/></svg>',
// 复杂图标
'brand-logo': `
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="currentColor"/>
<text x="50" y="55" text-anchor="middle" fill="white" font-size="20">LOGO</text>
</svg>
`,
// 多色图标
'colorful-icon': `
<svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" fill="#3b82f6"/>
<path d="M8 12l2 2 4-4" stroke="white" stroke-width="2" fill="none"/>
</svg>
`,
}
// 配置
export default defineConfig({
presets: [
presetIcons({
collections: {
custom: customIcons,
},
}),
],
})
2. 动态图标加载
// 动态加载图标
const dynamicIcons = {
async loadIcon(name) {
try {
const response = await fetch(`/icons/${name}.svg`)
return await response.text()
} catch (error) {
console.warn(`Icon ${name} not found`)
return null
}
},
}
// 使用文件系统图标
import { promises as fs } from 'fs'
import { join } from 'path'
const fileSystemIcons = {
async loadIcon(name) {
try {
const iconPath = join(process.cwd(), 'assets/icons', `${name}.svg`)
return await fs.readFile(iconPath, 'utf-8')
} catch (error) {
return null
}
},
}
Web字体集成
Web字体预设
// uno.config.js
import { defineConfig, presetWebFonts, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetWebFonts({
// Google Fonts
provider: 'google',
fonts: {
// 无衬线字体
sans: 'Inter:400,500,600,700',
serif: 'Merriweather:400,700',
mono: 'JetBrains Mono:400,500,700',
// 自定义字体名称
heading: 'Poppins:600,700,800',
body: 'Open Sans:400,600',
// 多个字体备选
display: [
'Playfair Display:400,700',
'serif',
],
},
// 配置选项
extendTheme: true, // 扩展主题
inlineImports: true, // 内联导入
themeKey: 'fontFamily', // 主题键名
}),
],
})
字体使用方法
<!-- 基础字体 -->
<h1 class="font-heading text-4xl font-bold">标题字体</h1>
<p class="font-body text-base">正文字体</p>
<code class="font-mono text-sm">代码字体</code>
<!-- 字体粗细 -->
<div class="font-sans">
<p class="font-normal">正常粗细</p>
<p class="font-medium">中等粗细</p>
<p class="font-semibold">半粗体</p>
<p class="font-bold">粗体</p>
</div>
<!-- 响应式字体 -->
<h1 class="font-heading text-2xl sm:text-3xl md:text-4xl lg:text-5xl">
响应式标题
</h1>
<!-- 字体样式组合 -->
<article class="font-body">
<h1 class="font-heading font-bold text-3xl mb-4">文章标题</h1>
<p class="text-lg leading-relaxed text-gray-700 mb-6">
文章内容使用易读的正文字体...
</p>
<code class="font-mono bg-gray-100 px-2 py-1 rounded text-sm">
代码片段
</code>
</article>
自定义字体提供商
// 自定义字体提供商
const customFontProvider = {
name: 'custom',
async getFontURL(font) {
// 自定义字体URL生成逻辑
return `https://fonts.example.com/${font.name}/${font.weights.join(',')}.css`
},
}
export default defineConfig({
presets: [
presetWebFonts({
provider: customFontProvider,
fonts: {
custom: 'CustomFont:400,700',
},
}),
],
})
排版预设
排版预设配置
// uno.config.js
import { defineConfig, presetTypography, presetUno } from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetTypography({
// 配置选项
cssExtend: {
// 自定义排版样式
'h1': {
'font-size': '2.5rem',
'line-height': '1.2',
'margin-bottom': '1rem',
},
'p': {
'margin-bottom': '1rem',
'line-height': '1.6',
},
'code': {
'background-color': '#f3f4f6',
'padding': '0.125rem 0.25rem',
'border-radius': '0.25rem',
'font-size': '0.875rem',
},
},
}),
],
})
排版类使用
<!-- 基础排版 -->
<article class="prose">
<h1>文章标题</h1>
<p>这是一个段落,包含了基础的排版样式。</p>
<h2>二级标题</h2>
<p>另一个段落,展示了排版预设的效果。</p>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
<blockquote>
这是一个引用块,展示了引用的样式。
</blockquote>
<pre><code>const code = 'example';</code></pre>
</article>
<!-- 不同尺寸的排版 -->
<article class="prose prose-sm">小尺寸排版</article>
<article class="prose prose-lg">大尺寸排版</article>
<article class="prose prose-xl">超大尺寸排版</article>
<!-- 暗色模式排版 -->
<article class="prose dark:prose-invert">
<h1>暗色模式标题</h1>
<p>暗色模式下的段落文本。</p>
</article>
<!-- 自定义颜色排版 -->
<article class="prose prose-blue">
<h1>蓝色主题排版</h1>
<p>使用蓝色主题的排版样式。</p>
</article>
插件系统
插件基础概念
插件是扩展 UnoCSS 功能的模块,可以添加新的预设、规则、变体等。
// 基础插件结构
function myPlugin(options = {}) {
return {
name: 'my-plugin',
// 添加规则
rules: [
['my-rule', { color: 'red' }],
],
// 添加变体
variants: [
(matcher) => {
if (matcher.startsWith('my:')) {
return {
matcher: matcher.slice(3),
selector: s => `.my-context ${s}`,
}
}
return matcher
},
],
// 添加快捷方式
shortcuts: {
'my-btn': 'px-4 py-2 bg-blue-500 text-white rounded',
},
// 主题扩展
theme: {
colors: {
primary: '#3b82f6',
},
},
// 预处理器
preprocess(matcher) {
// 预处理逻辑
return matcher
},
// 后处理器
postprocess(util) {
// 后处理逻辑
return util
},
}
}
// 使用插件
export default defineConfig({
plugins: [
myPlugin({
// 插件选项
}),
],
})
实用插件开发
1. 动画插件
// 动画插件
function animationPlugin(options = {}) {
const animations = {
'fade-in': {
'animation': 'fadeIn 0.5s ease-in-out',
'@keyframes fadeIn': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
},
'slide-up': {
'animation': 'slideUp 0.3s ease-out',
'@keyframes slideUp': {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
'bounce-in': {
'animation': 'bounceIn 0.6s ease-out',
'@keyframes bounceIn': {
'0%': { transform: 'scale(0.3)', opacity: '0' },
'50%': { transform: 'scale(1.05)' },
'70%': { transform: 'scale(0.9)' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
},
}
return {
name: 'animation-plugin',
rules: [
[/^animate-(.+)$/, ([, name]) => {
return animations[name] || {}
}],
],
}
}
// 使用方式
// <div class="animate-fade-in">淡入动画</div>
// <div class="animate-slide-up">滑入动画</div>
2. 主题插件
// 主题插件
function themePlugin(themes = {}) {
return {
name: 'theme-plugin',
variants: [
(matcher) => {
for (const themeName of Object.keys(themes)) {
if (matcher.startsWith(`${themeName}:`)) {
return {
matcher: matcher.slice(themeName.length + 1),
selector: s => `[data-theme="${themeName}"] ${s}`,
}
}
}
return matcher
},
],
rules: [
// 主题颜色规则
[/^theme-(.+)$/, ([, colorName]) => {
return { color: `var(--theme-${colorName})` }
}],
[/^bg-theme-(.+)$/, ([, colorName]) => {
return { 'background-color': `var(--theme-${colorName})` }
}],
],
// 生成CSS变量
preflights: [
{
getCSS() {
let css = ''
for (const [themeName, colors] of Object.entries(themes)) {
css += `[data-theme="${themeName}"] {\n`
for (const [colorName, colorValue] of Object.entries(colors)) {
css += ` --theme-${colorName}: ${colorValue};\n`
}
css += '}\n'
}
return css
},
},
],
}
}
// 使用示例
const themes = {
light: {
primary: '#3b82f6',
secondary: '#6b7280',
background: '#ffffff',
text: '#111827',
},
dark: {
primary: '#60a5fa',
secondary: '#9ca3af',
background: '#111827',
text: '#f9fafb',
},
}
export default defineConfig({
plugins: [
themePlugin(themes),
],
})
// HTML使用
// <div data-theme="light">
// <p class="theme-text bg-theme-background">浅色主题</p>
// </div>
// <div data-theme="dark">
// <p class="theme-text bg-theme-background">深色主题</p>
// </div>
3. 组件插件
// 组件插件
function componentPlugin(components = {}) {
return {
name: 'component-plugin',
shortcuts: Object.entries(components).reduce((acc, [name, styles]) => {
acc[name] = styles
return acc
}, {}),
rules: [
// 组件变体
[/^(.+)-variant-(.+)$/, ([, component, variant]) => {
const componentConfig = components[component]
if (componentConfig && componentConfig.variants && componentConfig.variants[variant]) {
return componentConfig.variants[variant]
}
return {}
}],
],
}
}
// 组件定义
const components = {
'btn': 'inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-md transition-colors',
'card': 'bg-white rounded-lg shadow border border-gray-200 p-6',
'input': 'block w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2',
// 带变体的组件
'alert': {
base: 'p-4 rounded-md border',
variants: {
info: { 'background-color': '#dbeafe', 'border-color': '#3b82f6', color: '#1e40af' },
success: { 'background-color': '#dcfce7', 'border-color': '#22c55e', color: '#166534' },
warning: { 'background-color': '#fef3c7', 'border-color': '#f59e0b', color: '#92400e' },
error: { 'background-color': '#fee2e2', 'border-color': '#ef4444', color: '#991b1b' },
},
},
}
export default defineConfig({
plugins: [
componentPlugin(components),
],
})
// 使用方式
// <button class="btn">基础按钮</button>
// <div class="card">卡片内容</div>
// <div class="alert alert-variant-success">成功提示</div>
插件生态系统
1. 官方插件
// 常用官方插件
import {
defineConfig,
presetUno, // 基础预设
presetAttributify, // 属性化模式
presetIcons, // 图标系统
presetWebFonts, // Web字体
presetTypography, // 排版
presetWind, // Tailwind CSS 兼容
presetMini, // 最小预设
transformerDirectives, // 指令转换器
transformerVariantGroup, // 变体组转换器
transformerCompileClass, // 编译类转换器
} from 'unocss'
export default defineConfig({
presets: [
presetUno(),
presetAttributify(),
presetIcons(),
presetWebFonts({
fonts: {
sans: 'Inter',
mono: 'JetBrains Mono',
},
}),
presetTypography(),
],
transformers: [
transformerDirectives(),
transformerVariantGroup(),
transformerCompileClass(),
],
})
2. 社区插件
// 社区插件示例
import { defineConfig } from 'unocss'
import { presetScrollbar } from 'unocss-preset-scrollbar'
import { presetAnimations } from 'unocss-preset-animations'
import { presetForms } from 'unocss-preset-forms'
export default defineConfig({
presets: [
// 滚动条样式
presetScrollbar(),
// 动画预设
presetAnimations(),
// 表单样式
presetForms(),
],
})
转换器系统
指令转换器
/* CSS指令 */
.custom {
@apply flex items-center justify-center;
@apply bg-blue-500 text-white;
@apply hover:bg-blue-600 transition-colors;
}
/* 编译后 */
.custom {
display: flex;
align-items: center;
justify-content: center;
background-color: #3b82f6;
color: #ffffff;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.custom:hover {
background-color: #2563eb;
}
变体组转换器
<!-- 变体组语法 -->
<div class="hover:(bg-blue-500 text-white) focus:(ring-2 ring-blue-500)">
变体组
</div>
<!-- 编译后 -->
<div class="hover:bg-blue-500 hover:text-white focus:ring-2 focus:ring-blue-500">
变体组
</div>
<!-- 复杂变体组 -->
<div class="sm:(text-lg font-semibold) md:(text-xl font-bold) lg:(text-2xl font-extrabold)">
响应式变体组
</div>
编译类转换器
<!-- 编译类 -->
<div class="uno: flex items-center justify-center bg-blue-500 text-white p-4 rounded-lg">
编译类
</div>
<!-- 编译后生成唯一类名 -->
<div class="uno-abc123">
编译类
</div>
<style>
.uno-abc123 {
display: flex;
align-items: center;
justify-content: center;
background-color: #3b82f6;
color: #ffffff;
padding: 1rem;
border-radius: 0.5rem;
}
</style>
本章总结
通过本章学习,我们深入了解了:
- 属性化模式:提高HTML可读性的新写法
- 图标系统:集成多种图标库的强大功能
- Web字体:简化字体集成和管理
- 排版预设:专业的文档排版样式
- 插件系统:扩展UnoCSS功能的核心机制
- 转换器:增强开发体验的工具
核心要点
- 属性化模式让HTML更加语义化和易读
- 图标系统提供了统一的图标管理方案
- 插件系统是UnoCSS生态的核心
- 转换器提供了更好的开发体验
下一步
在下一章中,我们将学习UnoCSS的性能优化技巧和最佳实践。
练习题
- 属性化重构:将传统class写法转换为属性化模式
- 自定义图标库:创建项目专用的图标集合
- 主题插件:开发支持多主题切换的插件
- 组件系统:使用插件创建完整的组件库
- 性能测试:对比不同特性的性能影响