unocss 的 rules 必须写在 uno.config.ts 中,以二维数组形式定义 matcher 和 handler;仅静态 class 字符串会被提取,修改后需重启开发服务器。

UnoCSS 的 rules 配置必须写在 uno.config.ts 里
不是放在 vite.config.ts、main.ts 或任何组件里,只有 uno.config.ts 中的 rules 数组才会被 UnoCSS 引擎识别并参与匹配。如果你把规则写在别处,它完全不会生效,页面上用的 class 会被忽略,也不报错——这是最常被踩的静默失效点。
配置结构固定为二维数组,每个子项是 [matcher, handler] 形式:
-
matcher可以是字符串(精确匹配)、正则(动态提取)、或函数(自定义逻辑) -
handler必须返回一个 CSS 属性对象,如{ margin: '4px' };返回null或undefined表示不生成样式
示例:支持 m-2、m-1.5、m-0p5 三种写法的 margin 规则
rules: [
[/^m-(\d+\.?\d*)(?:p(\d+))?$/, ([_, num, p]) => {
const value = p ? `${num}.${p}` : num
return { margin: `${value}px` }
}]
]
正则 matcher 中的捕获组顺序必须和 handler 参数一一对应
UnoCSS 会把正则匹配结果整个传给 handler,第一个参数是完整匹配串,后续才是括号捕获组。写错顺序会导致 num 是 undefined,最终生成空样式或 NaN 值。
常见错误写法:([/^m-(\d+)$/, (num) => ({ margin: `${num}px` })]) —— 这里 num 实际是完整匹配串(如 "m-4"),不是数字
正确写法必须显式解构:
[/^m-(\d+)$/, ([_, num]) => ({ margin: `${num}px` })][/^text-(\w+)$/, ([_, color]) => ({ color: theme.colors?.[color] || color })]
注意:下划线 _ 是惯用占位符,代表你不需要的第一个参数(全匹配串),不能省略,否则后续参数全部偏移。
动态规则生成后,class 必须在源码中「真实出现」才会被提取
UnoCSS 是纯构建时按需提取,不扫描 JS 对象、不运行模板函数、不解析字符串拼接。下面这些写法都不会触发规则生成:
const cls = 'm-2'; <div class="{cls}">(JSX / Vue SFC 中的变量插值) <li><code><div :class="['m-2']">(Vue 响应式 class 绑定) <li> <code>element.classList.add('m-2')(DOM 操作)
只有字面量形式的 class="m-2" 或 class={'m-2'}(带单引号/双引号的静态字符串)才会被提取器捕获。如果你依赖运行时拼接,得改用 shortcuts 预先定义好组合类,或者用 extractors 自定义提取逻辑。
开发时修改 rules 后要重启 dev server 才能生效
UnoCSS 的规则注册发生在 Vite 插件初始化阶段,uno.config.ts 加载是一次性的。改完 rules 不重启,旧规则仍缓存在内存里,新规则不会加载,热更新也无反应。
尤其容易忽略的是:当你在已有项目中首次添加 rules,或从空数组改成非空,必须强制重启,否则连最基础的 m-1 都不会生成 CSS。
顺带一提:shortcuts 和 theme 修改也一样,只要涉及 uno.config.ts 导出内容变更,就该重启。











