本章概述
本章将深入探讨 UnoCSS 的性能优化策略和最佳实践,帮助你构建高性能、可维护的样式系统。
性能优化策略
构建时优化
1. 按需生成配置
// uno.config.js
import { defineConfig } from 'unocss'
export default defineConfig({
// 扫描文件配置
content: {
// 指定扫描的文件类型和路径
filesystem: [
'src/**/*.{vue,js,ts,jsx,tsx}',
'components/**/*.{vue,js,ts,jsx,tsx}',
'pages/**/*.{vue,js,ts,jsx,tsx}',
'!node_modules/**/*',
'!dist/**/*',
],
// 内联内容扫描
inline: [
// 扫描JS中的模板字符串
/class[Name]?\s*[:=]\s*["'`]([^"'`]*)["'`]/,
// 扫描动态类名
/className\s*[:=]\s*["'`]([^"'`]*)["'`]/,
],
},
// 排除不需要的预设
presets: [
presetUno({
// 只启用需要的功能
dark: 'media', // 或 'class'
attributify: false, // 如果不使用属性化模式
}),
],
// 优化规则匹配
rules: [
// 使用更精确的匹配模式
[/^m-([\d.]+)$/, ([, d]) => ({ margin: `${d}px` })],
[/^p-([\d.]+)$/, ([, d]) => ({ padding: `${d}px` })],
],
// 启用缓存
cache: {
// 缓存目录
dir: '.unocss-cache',
// 缓存策略
strategy: 'filesystem', // 或 'memory'
},
})
2. 预设优化
// 自定义轻量级预设
function createLightPreset() {
return {
name: 'light-preset',
// 只包含必要的规则
rules: [
// 布局
['flex', { display: 'flex' }],
['block', { display: 'block' }],
['inline', { display: 'inline' }],
['hidden', { display: 'none' }],
// 常用间距
[/^m-([0-9]+)$/, ([, d]) => ({ margin: `${d * 0.25}rem` })],
[/^p-([0-9]+)$/, ([, d]) => ({ padding: `${d * 0.25}rem` })],
// 常用颜色
[/^text-(red|blue|green|gray)-(\d+)$/, ([, color, num]) => {
const colors = {
red: { 500: '#ef4444', 600: '#dc2626' },
blue: { 500: '#3b82f6', 600: '#2563eb' },
green: { 500: '#10b981', 600: '#059669' },
gray: { 500: '#6b7280', 600: '#4b5563' },
}
return { color: colors[color]?.[num] }
}],
],
// 最小化变体
variants: [
// 只包含必要的伪类
(matcher) => {
if (matcher.startsWith('hover:')) {
return {
matcher: matcher.slice(6),
selector: s => `${s}:hover`,
}
}
if (matcher.startsWith('focus:')) {
return {
matcher: matcher.slice(6),
selector: s => `${s}:focus`,
}
}
return matcher
},
],
}
}
// 使用轻量级预设
export default defineConfig({
presets: [
createLightPreset(),
// 根据需要添加其他预设
],
})
3. 构建工具优化
// vite.config.js
import { defineConfig } from 'vite'
import UnoCSS from 'unocss/vite'
export default defineConfig({
plugins: [
UnoCSS({
// 开发模式优化
mode: process.env.NODE_ENV === 'development' ? 'per-module' : 'global',
// 生产模式优化
inspector: process.env.NODE_ENV === 'development',
// 预构建优化
configDeps: [
'uno.config.js',
'src/styles/theme.js',
],
}),
],
// CSS优化
css: {
postcss: {
plugins: [
// 生产环境CSS压缩
process.env.NODE_ENV === 'production' && require('cssnano')({
preset: 'default',
}),
].filter(Boolean),
},
},
// 构建优化
build: {
// CSS代码分割
cssCodeSplit: true,
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
// 分块策略
rollupOptions: {
output: {
manualChunks: {
// 将UnoCSS相关代码分离
unocss: ['unocss'],
},
},
},
},
})
运行时优化
1. 动态类名优化
// 优化前:每次都重新计算
function BadComponent({ status, size }) {
const className = `
px-4 py-2 rounded-md font-medium transition-colors
${status === 'primary' ? 'bg-blue-500 text-white hover:bg-blue-600' : ''}
${status === 'secondary' ? 'bg-gray-200 text-gray-800 hover:bg-gray-300' : ''}
${size === 'sm' ? 'text-sm px-3 py-1' : ''}
${size === 'lg' ? 'text-lg px-6 py-3' : ''}
`
return <button className={className}>按钮</button>
}
// 优化后:使用预定义映射
const buttonClasses = {
base: 'px-4 py-2 rounded-md font-medium transition-colors',
status: {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-500 text-white hover:bg-red-600',
},
size: {
sm: 'text-sm px-3 py-1',
md: '', // 默认尺寸
lg: 'text-lg px-6 py-3',
},
}
function GoodComponent({ status = 'primary', size = 'md' }) {
const className = [
buttonClasses.base,
buttonClasses.status[status],
buttonClasses.size[size],
].filter(Boolean).join(' ')
return <button className={className}>按钮</button>
}
// 更进一步:使用工具函数
import { clsx } from 'clsx'
function createButtonClass({ status, size, disabled, fullWidth }) {
return clsx(
// 基础样式
'px-4 py-2 rounded-md font-medium transition-colors',
// 状态样式
{
'bg-blue-500 text-white hover:bg-blue-600': status === 'primary',
'bg-gray-200 text-gray-800 hover:bg-gray-300': status === 'secondary',
'bg-red-500 text-white hover:bg-red-600': status === 'danger',
},
// 尺寸样式
{
'text-sm px-3 py-1': size === 'sm',
'text-lg px-6 py-3': size === 'lg',
},
// 状态修饰
{
'opacity-50 cursor-not-allowed': disabled,
'w-full': fullWidth,
}
)
}
2. 缓存策略
// 类名缓存
const classCache = new Map()
function getCachedClass(key, generator) {
if (classCache.has(key)) {
return classCache.get(key)
}
const className = generator()
classCache.set(key, className)
return className
}
// 使用示例
function CachedComponent({ variant, size }) {
const cacheKey = `${variant}-${size}`
const className = getCachedClass(cacheKey, () => {
return createButtonClass({ variant, size })
})
return <button className={className}>缓存按钮</button>
}
// React Hook 缓存
import { useMemo } from 'react'
function useButtonClass({ variant, size, disabled }) {
return useMemo(() => {
return createButtonClass({ variant, size, disabled })
}, [variant, size, disabled])
}
function OptimizedButton(props) {
const className = useButtonClass(props)
return <button className={className}>{props.children}</button>
}
CSS 输出优化
1. 原子化优化
// 配置原子化级别
export default defineConfig({
// 启用原子化
atomic: true,
// 自定义原子化规则
rules: [
// 避免重复的复合规则
['btn-primary', {
'background-color': '#3b82f6',
'color': '#ffffff',
'padding': '0.5rem 1rem',
'border-radius': '0.375rem',
'font-weight': '500',
'transition': 'background-color 0.15s',
}],
// 拆分为原子规则
['bg-primary', { 'background-color': '#3b82f6' }],
['text-white', { 'color': '#ffffff' }],
['px-4', { 'padding-left': '1rem', 'padding-right': '1rem' }],
['py-2', { 'padding-top': '0.5rem', 'padding-bottom': '0.5rem' }],
['rounded-md', { 'border-radius': '0.375rem' }],
['font-medium', { 'font-weight': '500' }],
['transition-colors', { 'transition': 'background-color 0.15s' }],
],
})
2. CSS 压缩优化
// 自定义CSS后处理
export default defineConfig({
postprocess: [
// 移除重复声明
(util) => {
if (util.entries.length > 1) {
const seen = new Set()
util.entries = util.entries.filter(([prop, value]) => {
const key = `${prop}:${value}`
if (seen.has(key)) {
return false
}
seen.add(key)
return true
})
}
return util
},
// 优化简写属性
(util) => {
const entries = util.entries
const props = Object.fromEntries(entries)
// 合并margin属性
if (props['margin-top'] && props['margin-right'] &&
props['margin-bottom'] && props['margin-left']) {
const values = [
props['margin-top'],
props['margin-right'],
props['margin-bottom'],
props['margin-left']
]
if (values.every(v => v === values[0])) {
// 四个值相同
util.entries = [['margin', values[0]]]
} else if (values[0] === values[2] && values[1] === values[3]) {
// 上下相同,左右相同
util.entries = [['margin', `${values[0]} ${values[1]}`]]
}
}
return util
},
],
})
最佳实践
项目结构最佳实践
1. 配置文件组织
project/
├── uno.config.js # 主配置文件
├── styles/
│ ├── presets/
│ │ ├── base.js # 基础预设
│ │ ├── components.js # 组件预设
│ │ └── utilities.js # 工具类预设
│ ├── themes/
│ │ ├── light.js # 浅色主题
│ │ ├── dark.js # 深色主题
│ │ └── index.js # 主题入口
│ └── plugins/
│ ├── animations.js # 动画插件
│ ├── components.js # 组件插件
│ └── utilities.js # 工具插件
└── src/
├── components/
└── styles/
├── base.css # 基础样式
├── components.css # 组件样式
└── utilities.css # 工具样式
2. 模块化配置
// styles/presets/base.js
export const basePreset = {
name: 'base',
rules: [
// 基础规则
],
variants: [
// 基础变体
],
shortcuts: {
// 基础快捷方式
},
}
// styles/presets/components.js
export const componentPreset = {
name: 'components',
shortcuts: {
'btn': 'px-4 py-2 rounded-md font-medium transition-colors',
'btn-primary': 'btn bg-blue-500 text-white hover:bg-blue-600',
'btn-secondary': 'btn bg-gray-200 text-gray-800 hover:bg-gray-300',
'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 focus:ring-blue-500',
},
}
// styles/themes/index.js
export const lightTheme = {
colors: {
primary: '#3b82f6',
secondary: '#6b7280',
background: '#ffffff',
surface: '#f9fafb',
text: '#111827',
},
}
export const darkTheme = {
colors: {
primary: '#60a5fa',
secondary: '#9ca3af',
background: '#111827',
surface: '#1f2937',
text: '#f9fafb',
},
}
// uno.config.js
import { defineConfig } from 'unocss'
import { basePreset } from './styles/presets/base.js'
import { componentPreset } from './styles/presets/components.js'
import { lightTheme, darkTheme } from './styles/themes/index.js'
export default defineConfig({
presets: [
basePreset,
componentPreset,
],
theme: {
colors: {
...lightTheme.colors,
// 主题切换逻辑
},
},
})
命名规范
1. 类名命名规范
// 推荐的命名模式
const namingConventions = {
// 布局类
layout: {
'container': 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
'section': 'py-12 sm:py-16 lg:py-20',
'grid-cols-auto': 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
},
// 组件类
components: {
'btn-base': 'inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-md transition-colors',
'card-base': 'bg-white rounded-lg shadow border border-gray-200',
'input-base': 'block w-full px-3 py-2 border border-gray-300 rounded-md',
},
// 状态类
states: {
'is-active': 'bg-blue-500 text-white',
'is-disabled': 'opacity-50 cursor-not-allowed',
'is-loading': 'animate-pulse',
},
// 工具类
utilities: {
'sr-only': 'position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;',
'truncate': 'overflow: hidden; text-overflow: ellipsis; white-space: nowrap;',
},
}
2. 变体命名规范
// 变体命名最佳实践
const variantNaming = {
// 响应式前缀
responsive: ['sm:', 'md:', 'lg:', 'xl:', '2xl:'],
// 状态前缀
states: [
'hover:', 'focus:', 'active:', 'disabled:',
'first:', 'last:', 'odd:', 'even:',
'visited:', 'checked:', 'invalid:',
],
// 伪元素前缀
pseudoElements: [
'before:', 'after:', 'placeholder:', 'selection:',
'first-line:', 'first-letter:',
],
// 组合变体
combinations: [
'sm:hover:', 'md:focus:', 'lg:active:',
'dark:hover:', 'group-hover:', 'peer-focus:',
],
}
团队协作规范
1. 代码审查清单
## UnoCSS 代码审查清单
### 性能检查
- [ ] 是否使用了不必要的复杂选择器
- [ ] 是否有重复的样式定义
- [ ] 动态类名是否进行了优化
- [ ] 是否启用了适当的缓存策略
### 可维护性检查
- [ ] 类名是否遵循项目命名规范
- [ ] 是否使用了语义化的快捷方式
- [ ] 复杂样式是否提取为组件类
- [ ] 是否有适当的注释说明
### 兼容性检查
- [ ] 响应式设计是否完整
- [ ] 暗色模式是否正确实现
- [ ] 浏览器兼容性是否满足要求
- [ ] 无障碍访问是否考虑
### 最佳实践检查
- [ ] 是否使用了原子化思维
- [ ] 组件样式是否合理封装
- [ ] 主题变量是否正确使用
- [ ] 构建配置是否优化
2. 样式指南
// 团队样式指南
const styleGuide = {
// 间距系统
spacing: {
description: '使用 4px 基础单位的间距系统',
scale: [0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64],
usage: {
'p-1': '4px padding',
'p-2': '8px padding',
'p-4': '16px padding',
'm-4': '16px margin',
},
},
// 颜色系统
colors: {
description: '使用语义化颜色名称',
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
semantic: {
success: 'green-500',
warning: 'yellow-500',
error: 'red-500',
info: 'blue-500',
},
},
// 字体系统
typography: {
description: '统一的字体大小和行高',
scale: {
'text-xs': { fontSize: '0.75rem', lineHeight: '1rem' },
'text-sm': { fontSize: '0.875rem', lineHeight: '1.25rem' },
'text-base': { fontSize: '1rem', lineHeight: '1.5rem' },
'text-lg': { fontSize: '1.125rem', lineHeight: '1.75rem' },
'text-xl': { fontSize: '1.25rem', lineHeight: '1.75rem' },
},
},
// 组件规范
components: {
button: {
base: 'inline-flex items-center justify-center font-medium transition-colors',
sizes: {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-sm',
lg: 'px-6 py-3 text-base',
},
variants: {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
outline: 'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
},
},
},
}
调试和测试
1. 调试工具
// 开发环境调试配置
export default defineConfig({
// 启用检查器
inspector: process.env.NODE_ENV === 'development',
// 详细日志
warn: true,
// 自定义调试规则
rules: [
// 调试规则:显示元素边界
['debug', {
'outline': '1px solid red',
'outline-offset': '-1px',
}],
// 调试规则:显示网格
['debug-grid', {
'background-image': 'linear-gradient(rgba(255,0,0,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,0,0,0.1) 1px, transparent 1px)',
'background-size': '20px 20px',
}],
],
// 开发时的额外预设
presets: [
// 生产预设
presetUno(),
// 开发调试预设
process.env.NODE_ENV === 'development' && {
name: 'debug',
shortcuts: {
'debug-red': 'outline outline-1 outline-red-500',
'debug-blue': 'outline outline-1 outline-blue-500',
'debug-green': 'outline outline-1 outline-green-500',
},
},
].filter(Boolean),
})
2. 测试策略
// 样式测试工具
class StyleTester {
constructor(config) {
this.config = config
this.generator = createGenerator(config)
}
// 测试类名生成
async testClassGeneration(className) {
const result = await this.generator.generate(className)
return {
input: className,
output: result.css,
matched: result.matched.size > 0,
}
}
// 批量测试
async testBatch(classNames) {
const results = []
for (const className of classNames) {
const result = await this.testClassGeneration(className)
results.push(result)
}
return results
}
// 性能测试
async performanceTest(classNames, iterations = 1000) {
const start = performance.now()
for (let i = 0; i < iterations; i++) {
await this.generator.generate(classNames.join(' '))
}
const end = performance.now()
return {
totalTime: end - start,
averageTime: (end - start) / iterations,
classCount: classNames.length,
}
}
}
// 使用示例
const tester = new StyleTester(unoConfig)
// 测试常用类名
const commonClasses = [
'flex', 'items-center', 'justify-center',
'bg-blue-500', 'text-white', 'p-4',
'rounded-lg', 'shadow-md', 'hover:bg-blue-600',
]
tester.testBatch(commonClasses).then(results => {
console.log('测试结果:', results)
})
// 性能测试
tester.performanceTest(commonClasses).then(result => {
console.log('性能测试:', result)
})
迁移策略
1. 从其他CSS框架迁移
// Tailwind CSS 迁移配置
export default defineConfig({
presets: [
// 使用 Wind 预设保持兼容性
presetWind(),
// 自定义迁移规则
{
name: 'tailwind-migration',
rules: [
// 处理 Tailwind 特有的类名
['space-y-4', {
'> * + *': {
'margin-top': '1rem',
},
}],
// 处理自定义 Tailwind 配置
[/^bg-brand-(.+)$/, ([, color]) => {
const brandColors = {
primary: '#3b82f6',
secondary: '#6b7280',
}
return { 'background-color': brandColors[color] }
}],
],
},
],
})
// Bootstrap 迁移映射
const bootstrapMapping = {
// 网格系统
'container': 'max-w-7xl mx-auto px-4',
'row': 'flex flex-wrap -mx-3',
'col': 'flex-1 px-3',
'col-6': 'w-1/2 px-3',
'col-md-6': 'w-full md:w-1/2 px-3',
// 按钮
'btn': 'inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded border transition-colors',
'btn-primary': 'bg-blue-500 text-white border-blue-500 hover:bg-blue-600',
'btn-secondary': 'bg-gray-500 text-white border-gray-500 hover:bg-gray-600',
// 工具类
'text-center': 'text-center',
'text-left': 'text-left',
'text-right': 'text-right',
'd-none': 'hidden',
'd-block': 'block',
'd-flex': 'flex',
}
2. 渐进式迁移
// 渐进式迁移策略
const migrationConfig = {
// 阶段1:基础工具类
phase1: {
include: ['spacing', 'colors', 'typography'],
exclude: ['components', 'complex-layouts'],
},
// 阶段2:布局和组件
phase2: {
include: ['flexbox', 'grid', 'positioning'],
migrate: ['buttons', 'forms', 'cards'],
},
// 阶段3:高级特性
phase3: {
include: ['animations', 'transforms', 'filters'],
optimize: ['bundle-size', 'performance'],
},
}
// 迁移辅助工具
class MigrationHelper {
constructor(fromFramework, toConfig) {
this.fromFramework = fromFramework
this.toConfig = toConfig
this.mappings = this.loadMappings(fromFramework)
}
// 转换类名
convertClassName(oldClassName) {
return this.mappings[oldClassName] || oldClassName
}
// 批量转换
convertFile(content) {
let converted = content
for (const [oldClass, newClass] of Object.entries(this.mappings)) {
const regex = new RegExp(`\\b${oldClass}\\b`, 'g')
converted = converted.replace(regex, newClass)
}
return converted
}
// 生成迁移报告
generateReport(files) {
const report = {
totalFiles: files.length,
convertedClasses: 0,
unconvertedClasses: [],
suggestions: [],
}
// 分析和统计逻辑
return report
}
}
监控和分析
性能监控
// 性能监控配置
const performanceMonitor = {
// 构建时监控
buildMetrics: {
// CSS文件大小
cssSize: {
threshold: 50 * 1024, // 50KB
alert: size => console.warn(`CSS size ${size} exceeds threshold`),
},
// 构建时间
buildTime: {
threshold: 5000, // 5秒
alert: time => console.warn(`Build time ${time}ms exceeds threshold`),
},
// 类名数量
classCount: {
threshold: 1000,
alert: count => console.warn(`Class count ${count} exceeds threshold`),
},
},
// 运行时监控
runtimeMetrics: {
// 样式计算时间
styleCalculation: {
measure: () => {
const start = performance.now()
// 样式计算逻辑
const end = performance.now()
return end - start
},
},
// 重绘次数
repaints: {
count: 0,
increment: () => performanceMonitor.runtimeMetrics.repaints.count++,
},
},
}
// 监控插件
function createMonitoringPlugin() {
return {
name: 'performance-monitor',
buildStart() {
this.startTime = Date.now()
},
buildEnd() {
const buildTime = Date.now() - this.startTime
performanceMonitor.buildMetrics.buildTime.alert(buildTime)
},
generateBundle(options, bundle) {
for (const [fileName, chunk] of Object.entries(bundle)) {
if (fileName.endsWith('.css')) {
const size = chunk.source.length
performanceMonitor.buildMetrics.cssSize.alert(size)
}
}
},
}
}
分析工具
// CSS分析工具
class CSSAnalyzer {
constructor(cssContent) {
this.css = cssContent
this.rules = this.parseRules()
}
// 解析CSS规则
parseRules() {
// CSS解析逻辑
return []
}
// 分析重复规则
findDuplicates() {
const duplicates = new Map()
for (const rule of this.rules) {
const key = JSON.stringify(rule.declarations)
if (duplicates.has(key)) {
duplicates.get(key).push(rule)
} else {
duplicates.set(key, [rule])
}
}
return Array.from(duplicates.entries())
.filter(([, rules]) => rules.length > 1)
}
// 分析未使用的规则
findUnused(htmlContent) {
const usedSelectors = this.extractSelectorsFromHTML(htmlContent)
const unusedRules = []
for (const rule of this.rules) {
if (!usedSelectors.has(rule.selector)) {
unusedRules.push(rule)
}
}
return unusedRules
}
// 生成优化建议
generateOptimizationSuggestions() {
const suggestions = []
// 检查重复规则
const duplicates = this.findDuplicates()
if (duplicates.length > 0) {
suggestions.push({
type: 'duplicates',
message: `Found ${duplicates.length} duplicate rule groups`,
impact: 'medium',
solution: 'Consider using atomic classes or CSS-in-JS',
})
}
// 检查复杂选择器
const complexSelectors = this.rules.filter(rule =>
rule.selector.split(' ').length > 3
)
if (complexSelectors.length > 0) {
suggestions.push({
type: 'complexity',
message: `Found ${complexSelectors.length} complex selectors`,
impact: 'low',
solution: 'Simplify selectors or use component classes',
})
}
return suggestions
}
}
本章总结
通过本章学习,我们掌握了:
- 构建时优化:配置优化、预设精简、缓存策略
- 运行时优化:动态类名优化、缓存机制
- CSS输出优化:原子化策略、压缩优化
- 项目结构:模块化配置、命名规范
- 团队协作:代码审查、样式指南
- 调试测试:调试工具、测试策略
- 迁移策略:框架迁移、渐进式升级
- 监控分析:性能监控、分析工具
核心要点
- 性能优化应该贯穿开发全流程
- 团队协作需要统一的规范和工具
- 监控和分析有助于持续改进
- 渐进式迁移降低风险
下一步
在下一章中,我们将探索UnoCSS的生态系统和社区资源。
练习题
- 性能优化:分析现有项目的CSS性能瓶颈
- 配置优化:创建适合团队的UnoCSS配置
- 迁移计划:制定从其他框架迁移的详细计划
- 监控系统:实现CSS性能监控系统
- 最佳实践:建立团队的样式开发规范