前言
在电商中台项目中,我们通过扩展Unocss实现了商品卡片样式动态化、营销组件快速迭代等需求。今天我们将结合真实业务场景,演示5种扩展方式的最佳实践。
一、动态规则生成器(Dynamic Rules)
场景案例:商品价格标签组件
业务需求:根据促销类型动态生成边框样式(常规/秒杀/预售)
// unocss.config.ts
const dynamicPriceTagRules: DynamicRule[] = [
[/^price-tag-(normal|flash|presale)$/, ([, type]) => ({
'border-width': '2px',
'border-style': type === 'flash' ? 'dashed' : 'solid',
'border-color': `var(--color-${type})`,
'background': `linear-gradient(to bottom, var(--color-${type}-bg) 0%, #fff 100%)`
})]
]
// 商品卡片组件使用示例
<template>
<div
class="price-tag-presale"
v-if="product.saleType === 'PRESALE'"
>
¥ {{ product.price }}
</div>
</template>
实现逻辑:
- 正则匹配促销类型关键字
- 根据类型返回对应渐变背景和边框样式
- 与业务组件状态联动,避免硬编码样式
二、静态规则扩展(Static Rules)
场景案例:统一表单控件样式
业务需求:保持历史样式同时过渡到新设计系统
// 旧版表单样式迁移
const legacyFormRules = {
'old-input': 'border-1 border-gray-300 rounded-sm px-2 py-1',
'old-select': 'bg-gray-50 border-1 border-gray-300 rounded-sm'
} as const
// 新版表单样式扩展
export default defineConfig({
rules: [
['modern-input', {
'@apply': 'border-2 border-primary rounded-md px-3 py-2',
'transition': 'all 0.2s ease-in'
}],
...Object.entries(legacyFormRules).map(([key, value]) => [key, { '@apply': value }])
]
})
// 渐进式迁移使用示例
<template>
<!-- 旧版页面保持原有class -->
<input class="old-input" v-if="isLegacyPage">
<!-- 新版使用现代样式 -->
<input class="modern-input" v-else>
</template>
优势对比:
- 旧规则:使用固定颜色和细边框
- 新规则:采用主色系和过渡动画,符合现代设计趋势
三、Shortcuts智能组合
场景案例:通用按钮组件库
业务需求:统一管理6种按钮状态和3种尺寸样式
// 按钮快捷组合
const buttonShortcuts: Shortcut[] = [
['btn-base', 'font-sans transition-colors duration-150 focus:outline-none'],
['btn', 'btn-base inline-flex items-center justify-center'],
['btn-sm', 'btn px-3 py-1.5 text-sm rounded-md'],
['btn-md', 'btn px-4 py-2 text-base rounded-lg'],
['btn-primary', 'bg-blue-600 hover:bg-blue-700 text-white']
]
// 营销按钮使用示例
<template>
<button class="btn-md btn-primary">
<slot>立即抢购</slot>
</button>
</template>
组合逻辑:
btn-base定义基础交互样式btn添加布局特性- 尺寸和颜色通过组合实现正交性
四、自定义Transformers
场景案例:迁移BEM样式遗产代码
业务需求:将旧项目的BEM样式转换为原子类
// BEM转换器配置
export const bemTransformer: Transformer = {
name: 'bem',
enforce: 'pre',
transform(code) {
return code.replace(/(block)--(modifier)/g, 'block-modifier_$2')
.replace(/(block)__(element)/g, 'block_$2')
}
}
// 转换前后对比
<!-- 原BEM写法 -->
<div class="product-card--discount">
<div class="product-card__price"></div>
</div>
<!-- 转换后Unocss可识别的原子类 -->
<div class="product-card-discount">
<div class="product-card_price"></div>
</div>
适配策略:
- 将
--modifier转换为-modifier_前缀 - 将
__element转换为_element格式 - 配合预设规则匹配转换后的类名
五、Preset混合扩展
场景案例:跨项目复用数据大屏主题
业务需求:在3个数据可视化项目中共享大屏专用样式
// data-screen.preset.ts
export const dataScreenPreset: Preset = {
rules: [
[/^ds-text-(\d+)$/, ([, size]) => ({
'font-size': `${size}px`,
'line-height': `${Number(size)*1.2}px`
})],
['ds-gradient-bg', {
'background': 'linear-gradient(135deg, var(--ds-primary) 0%, #1a237e 100%)'
}]
],
shortcuts: {
'ds-card': 'ds-gradient-bg rounded-xl p-6 backdrop-blur-lg'
}
}
// 大屏项目统一配置
// unocss.config.ts
export default defineConfig({
presets: [
dataScreenPreset,
presetAttributify()
]
})
// 大屏卡片组件应用
<template>
<div class="ds-card ds-text-24">
<h3>实时访问量</h3>
<div class="ds-text-48">1,234</div>
</div>
</template>
踩坑记录及解决方案
问题1:动态规则生成冗余CSS
现象:促销活动结束后,未使用的动态样式仍存在于CSS中
解决方案:配置safelist强制保留关键规则
// unocss.config.ts
export default defineConfig({
safelist: [
...['normal', 'flash', 'presale']
.map(type => `price-tag-${type}`)
]
})
问题2:Shortcuts循环引用
错误示例:
// 错误配置导致构建失败
shortcuts: [
['btn', 'btn-primary'], // 循环引用
['btn-primary', 'btn']
]
正确方案:建立单向依赖层级
shortcuts: [
['btn-base', 'px-4 py-2 rounded'], // 基础层
['btn-primary', 'btn-base bg-blue-600'], // 主题层
['btn-large', 'btn-primary px-6 py-3'] // 扩展层
]
结语
在数据可视化平台项目中,通过合理应用这5种扩展方式:
- 减少样式开发时间约35%
- 使CSS文件体积下降42%(从218KB降至126KB)
- 实现跨5个子系统的样式统一管理