Tailwind CSS V4 源文件类名检测完全指南(@source指令)
在 Tailwind CSS V3 时代,我们需要在 tailwind.config.js 的 content 数组手动配置扫描文件路径,告诉Tailwind去哪里找工具类。
Tailwind CSS V4 实现自动源文件扫描,不再需要配置content数组。底层会把源文件当做纯文本扫描,提取能够匹配工具类特征的字符串,只生成代码中实际用到的CSS,实现CSS体积最小化。同时新增CSS顶层指令 @source,用来接管扫描规则:增加扫描目录、忽略目录、强制生成类名、排除类名。
本文基于官方文档,讲解扫描原理、动态类名踩坑、@source全套语法、Monorepo场景、白名单/黑名单,配套可运行示例与避坑清单。
一、类名检测底层原理
Tailwind不会真正解析JSX/Vue模板、不会执行JS代码,它仅仅把所有源文件当做纯文本,使用正则匹配,提取看起来像工具类的文本片段。
关键点:它看不到运行时拼接出来的类名,只能看到源码里写死的完整字面字符串。
自动扫描的文件与自动忽略规则
V4自动扫描项目源码,但是会默认跳过下面这些文件/目录,无需手动配置:
- 在
.gitignore里面的全部文件与目录 node_modules依赖目录- 二进制文件:图片、视频、压缩包等
- CSS样式文件、lock锁文件
二、动态拼接类名大坑(高频踩坑)
因为Tailwind是编译期静态文本扫描,不执行JS,如果你用变量拼接生成部分类名,源码中不存在完整类名字符串,Tailwind不会生成对应的CSS,页面样式失效。
❌错误写法:拼接片段
HTML模板示例
<!-- 不要使用插值拼接类名片段 --> <div class="text-{{ isError ? 'red' : 'green' }}-600"></div>
React JSX示例
// 禁止:使用变量拼接类名片段 function Button({color}){ return <button className={`bg-${color}-500`}>按钮</button> }
✅正确写法:完整字面字符串,使用映射对象
HTML模板
<div class="{{ isError ? 'text-red-600' : 'text-green-600' }}"></div>
React JSX,映射表方案(官方推荐)
function Button({color,children}){ const variantMap = { blue:"bg-blue-600 hover:bg-blue-500 text-white", red:"bg-red-600 hover:bg-red-500 text-white", green:"bg-green-600 hover:bg-green-500 text-white" } return <button className={variantMap[color]}>{children}</button> }
原理:所有工具类完整字符串写在源码对象字面量中,Tailwind扫描文本可以识别全部类名,编译时生成CSS。
只有万不得已的时候,才使用
@source inline()强制生成类名,优先使用映射对象。
三、@source指令完整语法
V4全部在CSS中使用@source指令,替代V3 config中的content、safelist等配置,支持:添加扫描路径、忽略路径、设置扫描根目录、关闭自动扫描、强制生成类名、排除类名。
1. 添加额外扫描路径
场景:需要扫描被默认忽略的目录,例如npm内部组件库。路径相对于当前CSS文件。
@import "tailwindcss"; /* 扫描node_modules内的内部UI组件库 */ @source "../node_modules/@company/ui-lib";
2. 设置扫描基准目录 source(“路径”)
适用于Monorepo仓库,构建脚本运行在仓库根目录,而不是子项目目录,手动指定扫描根目录。
@import "tailwindcss" source("../src");
3. 忽略指定路径 @source not
某些目录不需要扫描,例如遗留旧业务代码,减少扫描耗时。
@import "tailwindcss"; @source not "../src/components/legacy";
4. 完全关闭自动扫描 source(none)
关闭全部自动扫描,所有扫描路径全部手动通过@source声明。适合多份tailwind样式文件,每份样式只生成自己需要的类名,互相隔离。
@import "tailwindcss" source(none); @source "../admin"; @source "../shared";
5. 强制生成类名 @source inline()(白名单,替代V3 safelist)
源码文本中找不到完整类名字符串,但运行时会使用,强制编译输出对应的CSS。这属于兜底手段,尽量少用。
生成单个工具类
@import "tailwindcss"; @source inline("underline");
同时生成变体 hover / focus
@import "tailwindcss"; @source inline("{hover:,focus:,}underline");
大括号展开语法,批量生成色阶工具
/* 生成bg-red-50 ~ bg-red-950,同时带hover变体 */ @source inline("{hover:,}bg-red-{50,{100..900..100},950}");
6. 排除类名 @source not inline()(黑名单)
即使源码扫描到了该类名,也强制不输出对应的CSS,用于精简CSS体积,剔除不需要的色系。
@import "tailwindcss"; @source not inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}");
四、@source使用场景总结
| 业务场景 | V4方案 |
|---|---|
| 普通业务项目 | 什么都不用写,V4自动扫描 |
| 扫描npm包里的组件库 | @source "../node_modules/xxx" |
| Monorepo多包项目,扫描目录错位 | @import "tailwindcss" source("../src") |
| 忽略旧代码目录,提升编译速度 | @source not "path" |
| 多套独立Tailwind样式,互相隔离 | source(none) + 手动@source |
| 运行时动态类名,源码无完整字符串(兜底) | @source inline() |
| 强制剔除部分不需要工具类,缩减CSS | @source not inline() |
五、V3 迁移对比
| 功能 | Tailwind V3 | Tailwind V4 |
|---|---|---|
| 扫描文件路径配置 | tailwind.config.js content数组 | 默认自动扫描;使用@source补充/调整 |
| 白名单强制生成类名 | config中 safelist | CSS @source inline() |
| 黑名单排除类名 | 无原生配置,需要purgeCSS额外处理 | CSS @source not inline() |
| 关闭全部自动扫描 | 不支持 | source(none) |
| 配置文件位置 | JS配置文件 | 全部写在CSS源码内 |
六、高频避坑清单
- ❌ 不要做字符串片段拼接类名,编译期只能识别源码完整字面字符串;优先用对象映射表。
- ⚠️
@source inline()属于兜底手段,不要滥用,大量使用会让CSS体积膨胀。 - ⚠️
@source的路径是相对于当前CSS文件,不是项目根目录,路径写错不会扫描到文件。 - ⚠️ CDN浏览器版本
@tailwindcss/browser@4不会执行文件扫描逻辑,全部工具类都会生成,@source相关指令在CDN环境无效果;@source仅在Vite / PostCSS / CLI构建NPM工程生效。 - ✅ Monorepo项目优先使用
source("../src")指定基准路径,解决扫描目录错位问题。 - ✅ node_modules会被默认忽略,扫描内部UI库需要手动加
@source。
总结
Tailwind CSS V4最大的变化就是取消强制的content配置,自动扫描源文件。底层只是文本扫描,无法识别运行时JS拼接的类名。
日常开发优先保证源码写完整工具类字面量;遇到特殊场景使用CSS顶层指令 @source:追加扫描路径、忽略路径、兜底强制生成类名、排除类名。
记住:@source inline()是兜底,不要当做常规手段。动态样式优先用对象映射表解决。
0 条笔记