Tailwind CSS JIT 模式未生效的最直接判断是终端未输出“Using JIT (Just-In-Time) mode”,说明监听器未启动,需检查版本、配置、content路径、构建工具集成及动态class处理。

Tailwind CSS JIT 模式在开发时不更新 CSS,基本可以断定它根本没进入 JIT 的 watch 流程——不是“慢”,是“没动”。终端没打印 Using JIT (Just-In-Time) mode,就等于你还在跑全量编译。
终端没显示 “Using JIT” 提示
这是最硬的判断依据。没有这行输出,说明 JIT 监听器压根没启动,所有保存操作都触发完整 CSS 重生成。
- 运行
npx tailwindcss -v,确认输出是v3.x.x(如v3.4.3);v2.x或残留@tailwindcss/jit包会强制降级 -
tailwind.config.js中若还存在mode: 'jit'、mode: 'aot'或purge字段,全部删掉——v3+ 已废弃,写了反而禁用 JIT - 启动命令里必须显式加
TAILWIND_MODE=watch,尤其在 Craco、Umi、Vite 自定义脚本中:"dev": "TAILWIND_MODE=watch vite"
content 路径没覆盖 HTML 或 Vue/TSX 文件
JIT 不猜代码在哪,只读 content 数组里写的路径。漏一个文件,对应 class 就彻底不进编译流水线。
-
content必须是数组,不能是字符串:["./src/**/*.{js,jsx,ts,tsx,vue}", "./public/index.html"]✅;"./src/**/*.tsx"❌ - Vue 项目必须包含
.vue后缀;Next.js 双路由需同时覆盖app/**和pages/**;Vite + TSX 项目漏写.tsx就会扫不到组件 -
public/index.html这类静态入口必须显式加入,否则里面写的class="text-lg"就不会被识别 - 避免
./**/*.js这种宽泛写法——它会扫描node_modules和dist,I/O 拖垮监听响应
构建工具没把变更推给 Tailwind
Tailwind JIT 不自己监听文件系统,它靠 Vite/CRACO/Webpack 把变更事件“推”过来。中间链路一断,保存就像往真空里喊话。
立即学习“前端免费学习笔记(深入)”;
- Vite 用户:确保没禁用
css.preprocess,且没用@import方式引入 Tailwind(该方式绕过 Vite 的 HMR 追踪) - Next.js 用户:**不要写
postcss.config.js**——它会覆盖框架内置配置,禁用 JIT 自动路径推导;真要自定义,确保tailwindcss是第一个插件 - CRACO 用户:检查
craco.config.js是否将tailwindcss正确注入 PostCSS 插件,并保留原始 CSS 规则处理逻辑 - Docker/Dev Container 场景:
inotify默认不可用,监听器收不到信号;加--poll参数或设TAILWIND_POLL=true
动态 class 拼接让 JIT 完全失效
JIT 是静态分析器,只认字符串字面量。模板字符串一出现,对应组合类就从生成结果里消失,热更新时既不新增也不更新。
-
className={`p-${size}-4`}→p-sm-4不生成;:class="`text-${type}-500`"(Vue)同理 - 改用条件对象:
className={size === 'sm' ? 'p-sm-4' : 'p-lg-4'} - 或用
clsx:className={clsx({ 'p-sm-4': size === 'sm' })} - 高频动态值可白名单兜底:
safelist: [/p-(sm|lg|xl)-4/],注意正则必须用斜杠包裹,'/p-sm-4/'会被当普通字符串处理
真正容易被忽略的是:safelist 修改后必须重启开发服务器,缓存不自动更新;而 content 配置再全也救不了动态拼接——它只决定“扫哪些文件”,不改变“怎么扫”。



















