@apply适用于已有工具类的语义化组合复用,如按钮、卡片等,需配合@layer components使用;addUtilities仅用于创建不存在的原子样式,必须通过插件系统实现且需手动处理响应式与深色模式。

直接用 @apply 封装多个工具类是最简单、最安全的实现方式;addUtilities 仅适用于无法用现有工具类组合出的底层原子样式,且必须配合插件系统使用。
什么时候该用 @apply 而不是插件?
绝大多数“复合类”需求其实不需要写插件——比如按钮、卡片、表单控件这类语义化组件,本质是已有工具类的组合复用。
- 写法直接:在全局 CSS 或组件 scoped 样式中用
@layer components+@apply - 自动继承响应式前缀(
md:p-4)、状态变体(hover:bg-blue-600)、深色模式(dark:bg-gray-800) - 不依赖构建配置,不涉及
content路径扫描,改完即生效 - 错误示例:
.btn {@apply bg-blue-500 text-white;}写在 HTML 的class属性里却没在任何content文件中出现——它仍会生成,因为@apply是 CSS 编译阶段行为,不走按需生成逻辑
addUtilities 适合什么场景?
当你需要一个**不存在于默认工具集中的原子级样式声明**,比如 .sr-only、.visually-hidden、或自定义 text-gradient,且这个类不能靠 @apply 拼出来时,才轮到 addUtilities。
- 必须用
tailwindcss/plugin包裹,否则addUtilities和theme()不可用 - 只接受扁平对象:
'.sr-only': { position: 'absolute', clip: 'rect(0 0 0 0)' },不支持&:hover或嵌套 - 响应式与深色模式不会自动附加,要手动展开:
'@media (min-width: 768px)': { '.md\:sr-only': { ... } },注意冒号前双反斜杠转义 - 深色模式需显式写选择器:
'.dark .dark\:bg-brand': { backgroundColor: theme('colors.brand.500') }
为什么 matchUtilities 比硬编码更可靠?
当你需要带参数的类,比如 aspect-16/9、text-fluid-2.5rem,硬编码所有可能值既冗长又不可维护。
立即学习“前端免费学习笔记(深入)”;
-
matchUtilities接收正则匹配逻辑和值映射函数,自动提取并转换参数 - 必须传
e参数处理特殊字符:[`.aspect-${e('16/9')}`]: { aspectRatio: '16/9' },否则生成的类名含非法 CSS 字符 - 启用响应式支持需显式设置
supportsVariants: true,否则sm:aspect-16/9不生效 -
theme()只能读取theme.extend中已声明的路径,未提前配置就调用会返回undefined
content 配置漏掉路径是静默失效的根源
哪怕插件写得完全正确,只要 tailwind.config.js 的 content 数组没覆盖到实际使用该类的文件(如 ./src/**/*.{js,ts,jsx,tsx,svelte}),类名就不会进入最终 CSS——浏览器里查不到,控制台也无报错。
- 本地插件通常放在
./plugins/下,但它的使用位置(比如 Svelte 组件的class="my-custom-class")必须被content扫描到 - 常见疏漏:忘了加
.svelte后缀、漏了src/lib目录、或用了错误 glob 模式(如src/**/*.svelte没包含src/routes/+page.svelte) - 验证方法:临时在某个被
content覆盖的文件里写一次该类名,看是否出现在构建后的 CSS 中



















