UnoCSS动态样式注入详解

2026-09-03 18:50:51 5 次阅读

UnoCSS 是一种按需生成 CSS 的原子化 CSS 引擎,与传统 CSS 框架提前准备大量样式类不同,它会根据项目实际使用到的工具类生成对应 CSS,因此具有体积小、响应速度快、配置灵活等特点。

实际开发中,静态 class 处理通常比较简单,例如 text-red-500p-4flex 等可以直接被 UnoCSS 扫描并生成样式。但当 class 名称来自变量、接口数据或用户操作时,动态样式注入就会成为一个容易踩坑的问题。

UnoCSS 动态样式注入是什么

所谓动态样式注入,通常是指程序运行过程中根据条件或数据动态决定使用哪些 UnoCSS 工具类,例如:


import { ref } from 'vue'

const color = ref('red')
const size = ref('lg')



    动态样式内容
  

从开发者角度看,这段代码最终可能生成:

动态样式内容

但 UnoCSS 的核心机制并不是等到浏览器运行这段 JavaScript 后再分析最终字符串,而主要依赖构建阶段的源码扫描。因此,类似 text-${color}-500 这样的动态 class,可能无法被静态扫描器准确识别。

这也是 UnoCSS 动态样式注入最常见的问题来源。

为什么动态 class 可能无法生效

UnoCSS 的按需生成机制决定了它需要知道项目中可能出现哪些工具类。

例如下面这种写法:

工具可以直接识别 text-red-500

但下面的代码:

实际 class 是运行时拼接出来的:

text-red-500
text-blue-500
text-green-500

构建工具在扫描源码时并不能始终可靠地推断 color 的所有可能取值。

因此,浏览器中可能出现:

但是最终 CSS 中并不存在:

.text-blue-500 {
  --un-text-opacity: 1;
  color: rgb(...);
}

结果就是 class 看起来已经添加成功,但页面样式没有变化。

这里需要明确一点:动态 class 绑定成功,不代表 UnoCSS 一定会生成对应 CSS。

推荐方案一:使用静态 class 映射

实际项目中,最推荐的做法通常不是直接拼接 UnoCSS class,而是建立一个明确的映射关系。

例如 Vue 项目可以这样处理:


import { computed, ref } from 'vue'

const status = ref('success')

const statusClass = computed(() => {
  const classMap = {
    success: 'text-green-500 bg-green-50',
    warning: 'text-yellow-500 bg-yellow-50',
    error: 'text-red-500 bg-red-50'
  }

  return classMap[status.value] || ''
})



    当前状态
  

这种方式最大的优势是 class 名称完整存在于源码中:

text-green-500
bg-green-50
text-yellow-500
bg-yellow-50
text-red-500
bg-red-50

UnoCSS 可以正常扫描这些内容,并按需生成 CSS。

这种方案特别适合按钮状态、标签颜色、消息类型、权限状态等场景。

推荐方案二:使用 safelist 强制生成动态样式

如果项目确实需要使用动态 class,可以通过 UnoCSS 的 safelist 明确告诉 UnoCSS 哪些工具类必须生成。

配置文件通常为:

// uno.config.ts
import { defineConfig } from 'unocss'

export default defineConfig({
  safelist: [
    'text-red-500',
    'text-green-500',
    'text-blue-500',
    'bg-red-500',
    'bg-green-500',
    'bg-blue-500'
  ]
})

这样,即使这些 class 没有直接出现在模板的静态文本中,UnoCSS 仍然会生成相应 CSS。

如果动态样式数量有限,这种方法非常实用。

例如:


const colors = ['red', 'green', 'blue']



    {{ color }}
  

可以将可能使用的 class 放入 safelist

需要注意的是,safelist 并不是越多越好。加入大量无实际使用场景的工具类,会削弱 UnoCSS 按需生成的优势。

推荐方案三:使用完整字符串集合

某些场景下,可以直接把可能使用的 class 保留为完整字符串集合。

例如:

const colorClasses = {
  red: 'text-red-500',
  blue: 'text-blue-500',
  green: 'text-green-500'
}

然后:

[color]"> 内容

这种方式比:

更加稳定。

原因在于前一种写法把实际的 UnoCSS 工具类完整暴露给扫描器,后者则依赖运行时字符串拼接。

对于业务系统而言,静态映射通常比任意字符串拼接更容易维护,也更容易进行类型约束。

推荐方案四:通过 CSS 变量处理真正动态的值

如果动态变化的是颜色、尺寸、间距等具体数值,而不是 UnoCSS 工具类本身,那么 CSS 变量通常是更合适的方案。

例如:


import { ref } from 'vue'

const color = ref('#409eff')


[var(--text-color)]"
    :style="{ '--text-color': color }"
  >
    动态颜色
  

这里 UnoCSS 只负责生成固定的:

text-[var(--text-color)]

真正变化的是 CSS 自定义属性:

--text-color

这种方式特别适合后端返回颜色值、主题色、用户自定义颜色等场景。

例如:

[var(--theme-color)]" :style="{ '--theme-color': themeColor }" > 主题内容

相比生成大量类似:

bg-[#ff0000]
bg-[#00ff00]
bg-[#0000ff]

的动态 class,CSS 变量可以避免产生大量不可预测的工具类。

动态尺寸同样可以使用 CSS 变量

假设后端返回一个动态宽度:

const width = 73

不建议直接构造:

`w-${width}`

因为 UnoCSS 的 w-73 是否存在、是否符合预期,还受到规则和单位体系影响。

可以改成:

[var(--progress-width)]" :style="{ '--progress-width': `${width}%` }" >

这样动态值由浏览器处理,而 UnoCSS 只需要生成固定规则。

UnoCSS 中使用动态任意值的注意事项

UnoCSS 支持类似以下形式的任意值:

[#ff0000]">
[137px]">
[rgb(20,30,40)]">

这些写法对于固定值非常方便。

但是,如果把整个 class 都通过变量生成,例如:

[${color}]`">

仍然可能面临静态扫描无法识别的问题。

因此:

[var(--color)]" :style="{ '--color': color }">

通常比:

[${color}]`">

更加可靠。

UnoCSS 动态样式注入与运行时注入的区别

需要区分两个概念:构建阶段生成 CSS浏览器运行时注入 CSS

UnoCSS 默认强调按需生成。开发环境中,UnoCSS 可以根据源码变化快速生成或更新对应 CSS,让开发者感觉像是“动态注入样式”。

但这并不意味着所有运行时产生的字符串 class 都会自动生成 CSS。

例如:

const className = `text-${userColor}-500`

userColor 来自接口:

{
  "userColor": "purple"
}

浏览器最终可能得到:

text-purple-500

如果构建阶段无法发现这个 class,UnoCSS 不一定会为它生成样式。

因此,UnoCSS 的“动态”主要体现在按需生成与开发体验,而不是无限制地支持任意运行时 class。

Vue 中动态样式的完整实践

一个常见的状态标签可以这样实现:


import { computed, ref } from 'vue'

const status = ref('success')

const statusClass = computed(() => {
  const styles = {
    success: 'text-green-600 bg-green-100',
    warning: 'text-orange-600 bg-orange-100',
    error: 'text-red-600 bg-red-100',
    info: 'text-blue-600 bg-blue-100'
  }

  return styles[status.value] || styles.info
})



    {{ status }}
  

这里将稳定的基础样式直接写在 class 中:

inline-flex rounded px-3 py-1 text-sm

把变化部分交给映射对象:

const styles = {
  success: 'text-green-600 bg-green-100',
  warning: 'text-orange-600 bg-orange-100',
  error: 'text-red-600 bg-red-100',
  info: 'text-blue-600 bg-blue-100'
}

这种结构既符合 UnoCSS 的工作方式,又能让业务逻辑保持清晰。

React 项目中的动态 UnoCSS 写法

React 中同样需要注意动态 class 的扫描问题。

不推荐:

function Button({ color }) {
  return (
    
      操作
    
  )
}

可以改为:

function Button({ color }) {
  const classMap = {
    red: 'bg-red-500',
    blue: 'bg-blue-500',
    green: 'bg-green-500'
  }

  return (
    [color] || 'bg-gray-500'}>
      操作
    
  )
}

如果动态值本身是任意颜色,则可以采用 CSS 变量:

function ColorBox({ color }) {
  return (
    
[var(--box-color)]" style={{ '--box-color': color }} > 内容
) }

这种设计可以将“样式结构”和“动态数据”分离。

使用规则生成动态样式

对于具有明确规律的业务场景,还可以通过 UnoCSS 的规则系统扩展自己的工具类。

例如:

import { defineConfig, presetUno } from 'unocss'

export default defineConfig({
  presets: [
    presetUno()
  ],
  rules: [
    [
      /^grid-cols-(d+)$/,
      ([, d]) => ({
        'grid-template-columns': `repeat(${d}, minmax(0, 1fr))`
      })
    ]
  ]
})

之后就可以使用:

这种方式适合项目中存在统一、明确的自定义工具类规范。

不过,自定义规则解决的是工具类生成规则问题,并不能完全解决运行时未知字符串无法被扫描的问题。对于接口返回的数据,仍然应该优先考虑映射或 CSS 变量。

动态样式注入常见问题排查

class 已经添加,但页面没有样式

首先打开浏览器开发者工具,检查元素:

如果 class 存在,再搜索最终加载的 CSS 是否包含对应规则。

如果 CSS 中不存在 .text-purple-500,基本可以判断为 UnoCSS 没有生成该工具类。

此时可以检查:

  • class 是否完全动态拼接;

  • 对应文件是否被 UnoCSS 扫描;

  • 是否使用了 safelist

  • 工具类是否写法正确;

  • 自定义规则是否正确匹配。

修改配置后样式仍然不生效

修改 uno.config.ts 后,建议重新启动开发服务器。

特别是新增:

safelist: [
  'text-purple-500'
]

或者修改扫描范围、预设和自定义规则后,不要只依赖热更新结果判断配置是否正确。

动态 class 来源于接口数据

例如:

const className = data.className

如果接口直接返回:

text-red-500

也不意味着 UnoCSS 能在构建阶段知道它。

对于后端可控的 class 集合,建议建立白名单:

const allowedClasses = {
  primary: 'text-blue-500',
  danger: 'text-red-500',
  success: 'text-green-500'
}

然后只根据业务字段选择:

const className = allowedClasses[data.type]

这种方式不仅解决 UnoCSS 扫描问题,也能避免让后端数据直接控制任意前端样式。

动态样式注入的选择建议

不同场景可以采用不同策略。

场景推荐方案
状态、类型、按钮样式静态 class 映射
少量动态工具类safelist
动态颜色值CSS 变量
动态尺寸值CSS 变量 + 任意值
自定义工具类体系UnoCSS Rules
后端返回样式类型白名单映射
完全未知的运行时 CSS原生 CSS 或运行时 CSS 方案

核心原则是:能静态确定的 class 尽量静态确定,真正动态的数值交给 CSS 变量处理。

性能方面需要注意什么

UnoCSS 的优势之一就是按需生成,因此动态样式设计也应该尽量保持可预测。

例如项目中有几千种可能的颜色:

text-red-100
text-red-200
...
text-purple-900
...

如果全部加入 safelist,最终生成的 CSS 体积会明显增加。

相比之下,如果实际需求只是用户选择任意颜色:

[var(--color)]" :style="{ '--color': userColor }" >