切换语言
切换主题

Tailwind 性能优化:JIT、content 配置与生产体积控制

凌晨三点,我盯着 Chrome DevTools 里那个 3.5MB 的 CSS 文件发呆。

刚上线的新功能页面,加载时间从 800ms 涨到了 3.2 秒。排查了一圈,发现问题就出在这个 CSS 文件上——里面塞满了我们根本没用到的 Tailwind 类名。

说实话,挺崩溃的。毕竟 Tailwind 号称”性能友好”,怎么反而拖后腿了?

后来才知道,问题不在 Tailwind,而在配置。只要理解了 JIT 模式的工作原理,把 content 配置调对,再加上生产构建的几层优化,CSS 文件就能从 MB 级压到 KB 级——Netflix 的网站甚至只用了 6.5KB。

今天把踩过的坑和你聊聊。


一、JIT 模式:Tailwind 的性能革命

1.1 传统模式的问题

那晚看到 3.5MB CSS 文件之前,我对 Tailwind 的理解还停留在”实用优先的 CSS 框架”这个层面。

后来才知道,Tailwind v2 之前的”传统模式”会预生成所有可能的类名组合——包括所有颜色、所有间距、所有变体(hover、focus、disabled 等)。一个中等复杂度的项目,开发环境 CSS 文件能达到 10MB 甚至更多。

10MB+
传统模式开发 CSS 体积

10MB 的 CSS 文件在本地开发看起来没什么——毕竟带宽不是问题。但浏览器要解析这么大的样式表,内存占用和 DevTools 性能都会受影响。

我之前在 Firefox 里调试时就遇到过:改动一个类名,DevTools 卡顿好几秒,刷新页面更是慢到怀疑人生。

更头疼的是,传统模式下很多配置都不敢改。比如你想加一个新断点或者启用 focus-visible 变体,脑子里得先算算”这会增加多少类名组合”。结果就是,团队被迫在性能和灵活性之间做选择。

1.2 JIT 到底是怎么工作的

JIT(Just-in-Time)模式在 Tailwind v2.1 引入,v3+ 默认启用。简单说就是:按需生成

传统模式会在构建时预生成所有可能的类名组合,哪怕你根本用不上。JIT 模式反过来——先扫描你的模板文件(HTML、JSX、Vue 等),找出实际使用了哪些类名,然后只生成这些样式。

举个例子。传统模式下,Tailwind 会生成类似这样的 CSS:

.bg-black { background-color: #000 }
.hover\:bg-black:hover { background-color: #000 }
.focus\:bg-black:focus { background-color: #000 }
.disabled\:bg-black:disabled { background-color: #000 }
/* ... 还有几十个变体组合 */

即使你的项目只用了一个 bg-black,其他几十个变体的样式也会被生成出来。

JIT 模式就不一样了。它扫描你的模板,发现只用了 bg-blackhover:bg-black,就只生成这两条:

.bg-black { background-color: #000 }
.hover\:bg-black:hover { background-color: #000 }

这样一来,开发环境的 CSS 文件从 10MB 级直接降到 KB 级——而且和生产环境的体积一样小。

1.3 JIT 的实际收益

我记得第一次在项目里启用 JIT 模式时,刷新页面快得有点不真实——之前要等好几秒,现在几乎是瞬间。

构建速度:之前完整构建要等 2-3 分钟,现在几秒钟搞定。因为 JIT 不需要预生成所有样式,只要扫描模板文件提取类名就够了。

开发体验:DevTools 不再卡顿了。之前修改一个类名,要等浏览器重新解析那个 10MB 的样式表;现在样式表就几 KB,改动几乎是即时生效的。

任意值支持:这是个意外惊喜。传统模式下,你想用 text-[#facc15] 这种任意值,得先在配置里启用 safelist。JIT 模式直接支持:

// 不需要在配置里预定义,直接用
<h1 class="text-[2.5rem] mt-[1.35rem] text-[#facc15]">
  JIT 让一切变得简单
</h1>

动态类名:传统模式下,像 'text-' + color 这种拼接的类名无法被检测到。JIT 模式虽然也有限制,但配合 safelist 能更灵活地处理动态场景。

说了这么多,你可能会想:那 JIT 模式怎么启用?其实现在(Tailwind v3+)默认就是 JIT,不用额外配置。如果你还在用 v2,可以这样启用:

// tailwind.config.js
module.exports = {
  mode: 'jit',  // v2 需要手动启用
  content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
  // ...
}

二、content 配置:精准扫描的关键

JIT 模式好用,但前提是 content 配置正确。

content 决定了 Tailwind 会扫描哪些文件来提取类名。配置不对,要么样式丢失(扫描范围太窄),要么 CSS 膨胀(扫描范围太宽)。

2.1 content 配置基础

基本语法很简单:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js,jsx,ts,tsx}',  // 扫描 src 目录下所有模板文件
  ],
  // ...
}

这里的 ** 表示任意层级目录,*.{html,js,jsx,ts,tsx} 表示匹配这些扩展名的文件。

如果你用的是特定框架,可能要调整路径:

// Next.js 项目
content: [
  './pages/**/*.{js,ts,jsx,tsx}',
  './components/**/*.{js,ts,jsx,tsx}',
  './app/**/*.{js,ts,jsx,tsx}',  // App Router
]

// Astro 项目
content: [
  './src/**/*.{astro,html,js,jsx,ts,tsx}',
]

重点是:覆盖所有使用了 Tailwind 类名的文件。遗漏一个目录,那个目录里的样式就不会生成。

2.2 我踩过的坑

坑一:过宽的 glob 模式

我当时为了让配置”简单”,用了这样的写法:

content: [
  './**/*.js',  // 这会扫描 node_modules!
]

结果 Tailwind 把 node_modules 里所有 JS 文件都扫描了一遍,构建时间暴涨,还生成了一堆莫名其妙的样式。

正确的做法是限制范围:

content: [
  './src/**/*.js',      // 只扫描 src 目录
  './components/**/*.js', // 只扫描 components 目录
]

坑二:遗漏组件目录

有次重构项目,把组件挪到了 ./lib/components/ 目录。忘了更新 content 配置,结果新位置的组件样式全丢了。

排查半天才发现问题。教训就是:改项目结构时,一定要同步更新 content 配置

坑三:动态构建的类名

像这种写法:

const color = 'red';
const className = `text-$&#123;color&#125;-500`;  // JIT 无法检测到

JIT 扫描时看到的是模板字符串,不是完整的类名。结果生产构建时这条样式被删了。

解决办法是用 safelist(后面会讲)或者改用对象语法:

const colors = {
  red: 'text-red-500',
  blue: 'text-blue-500',
};
const className = colors[color];  // 完整类名,能被检测到

2.3 safelist 与动态类名

有些场景确实需要动态类名,这时候 safelist 就派上用场了。

// tailwind.config.js
module.exports = {
  safelist: [
    'text-red-500',
    'text-blue-500',
    'bg-red-500',
    // 或者用正则匹配一组类名
    {
      pattern: /text-(red|blue|green)-(500|600)/,
      variants: ['hover', 'focus'],  // 同时保留变体
    },
  ],
}

safelist 会强制 Tailwind 生成这些样式,即使模板文件里没直接出现。

但要注意:safelist 越多,CSS 文件越大。只在必要场景使用,别把它当作”保险箱”把所有可能用到的类名都塞进去。


三、生产体积控制:四层优化策略

JIT 模式让开发环境的小体积 CSS 成为可能,但生产构建还需要进一步优化。

我整理了一个四层优化策略,从配置到压缩层层递进。

第一层
精准 content 配置
只扫描实际使用的文件
第二层
PurgeCSS 移除
自动删除未使用样式
第三层
cssnano Minify
CSS 压缩优化
第四层
Brotli/Gzip
网络传输压缩
数据来源: 优化策略层级

3.1 第一层:精准 content 配置

这是最基础的,前面已经说过。

核心原则:只扫描实际使用 Tailwind 类名的文件。范围越精准,构建越快,CSS 越小。

// 好:精准范围
content: [
  './src/components/**/*.jsx',
  './src/pages/**/*.tsx',
]

// 差:范围太宽
content: [
  './**/*.js',  // 会扫描 node_modules
]

3.2 第二层:PurgeCSS 自动移除

Tailwind v3+ 在生产构建时自动启用 PurgeCSS,移除未使用的样式。

关键是确保构建命令正确区分开发/生产环境:

# 开发构建(不会移除未使用样式)
npm run dev

# 生产构建(自动 PurgeCSS)
npm run build

如果你用的是 PostCSS,可以在配置里显式控制:

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
    ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
  },
}

3.3 第三层:cssnano Minify

PurgeCSS 移除未使用的样式后,cssnano 进一步压缩 CSS。

压缩包括:移除注释、合并重复规则、简化选择器、压缩数值等。

# Tailwind CLI 直接 minify
npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify

# 或者通过 PostCSS(上面已经展示了)

一个实际数据:我的项目从 150KB(PurgeCSS 后)压到 45KB(minify 后)。

3.4 第四层:Brotli/Gzip 网络压缩

这一层不是 CSS 本身的优化,而是网络传输层面的。

服务器用 Brotli 或 Gzip 压缩静态资源,能让 CSS 文件再缩小 60-80%。

# Nginx 配置 Brotli(需要安装 ngx_brotli 模块)
brotli on;
brotli_comp_level 6;
brotli_types text/css application/javascript;

# 或者用 Gzip(Nginx 默认支持)
gzip on;
gzip_comp_level 6;
gzip_types text/css application/javascript;
6.5KB
Netflix CSS 传输体积

对比数据:45KB 的 CSS 文件,Brotli 压缩后传输体积约 8KB。

所以你看到 Netflix 说他们的 CSS 只有 6.5KB,指的是 Brotli 压缩后的传输体积,不是原始文件大小。


四、实战案例与数据对比

4.1 我的优化前后对比

3.5MB → 28KB
开发构建 CSS
JIT 模式开启前后
320KB → 48KB
生产构建 CSS
未压缩体积
7.2KB
最终传输体积
Brotli 压缩后
3.2s → 820ms
Lighthouse LCP
页面加载性能
数据来源: 实测数据

说实话,看到这个数据对比时,我自己都挺惊讶的。

4.2 常见问题排查

样式丢失了怎么办?

第一步:检查 content 配置,确认所有使用 Tailwind 类名的文件都在扫描范围内。

第二步:如果有动态类名,检查是否需要 safelist。

第三步:检查构建命令,确认用的是生产构建(npm run build),不是开发构建。

CSS 还是很大怎么办?

检查 safelist 是否过多。检查是否有第三方库的 CSS 混入 Tailwind 构建产物。检查 content 范围是否过宽。

DevTools 卡顿怎么办?

确认 Tailwind 版本 ≥3(JIT 默认启用)。检查 content 配置是否精准。如果还在用 v2,升级或手动启用 JIT 模式。


五、Tailwind v4 新特性展望

聊完现有优化,再看看 Tailwind v4(2024 年底发布)的新变化。

5.1 Oxide 引擎

这是最让人兴奋的更新:Tailwind 用 Rust 重写了底层引擎。

182x
增量构建速度提升

官方数据:增量构建速度提升 182 倍。说白了,之前改一个类名要等几秒重新编译,现在基本是毫秒级响应。

原理是 Oxide 引擎会缓存文件扫描结果,只重新处理改动的部分。这对大型项目特别有用——我们有个项目 200+ 组件文件,之前每次构建要等 10 秒左右,现在基本秒级完成。

5.2 零配置目标

Tailwind v4 推着一个新理念:大多数项目不需要 tailwind.config.js 了。

默认配置已经覆盖了常见需求:现代 CSS 特性、容器查询、合理的断点、完整的颜色系统。只有在需要深度定制时才写配置文件。

这意味着新项目上手更快,配置出错的机会也更少。

不过迁移到 v4 要注意:配置语法有变化。比如之前的 theme.extend.colors 现在要用 CSS 自定义属性写。迁移前最好看一遍官方升级指南。


总结

那次凌晨三点的崩溃之后,我把项目的 Tailwind 配置从头梳理了一遍。

JIT 模式让开发和生产用同样小的 CSS,content 配置决定了扫描范围,四层优化把 CSS 从 MB 压到 KB。加上 Tailwind v4 的 Oxide 引擎,构建速度再也不是瓶颈了。

如果你的项目还在用传统模式,或者 content 配置有疑问,建议先检查这几个点:

  1. Tailwind 版本是否 ≥3(JIT 默认启用)
  2. content 配置是否精准覆盖所有模板文件
  3. 生产构建是否启用 PurgeCSS 和 cssnano
  4. 服务器是否配置 Brotli/Gzip 压缩

改完这些,大概率能看到明显的性能提升。

有问题随时留言,也可以看看 Tailwind 官方文档的 JIT 和生产优化章节,写得挺清楚的。


Tailwind CSS 性能优化配置

从 JIT 模式启用到生产体积控制的完整配置流程

⏱️ 预计耗时: 30 分钟

  1. 1

    步骤1: 确认 Tailwind 版本

    检查项目 Tailwind CSS 版本:

    • Tailwind v3+ 默认启用 JIT 模式
    • Tailwind v2 需手动在配置中添加 mode: 'jit'
    • 建议升级到 v3+ 以获得最佳性能
  2. 2

    步骤2: 配置 content 扫描路径

    在 tailwind.config.js 中配置精准的扫描路径:

    • Next.js: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}']
    • Astro: ['./src/**/*.{astro,html,js,jsx,ts,tsx}']
    • 避免使用 './**/*.js' 这种过宽的 glob 模式
  3. 3

    步骤3: 处理动态类名

    对于动态构建的类名,使用 safelist 强制生成:

    • 在 safelist 数组中添加完整类名
    • 使用 pattern 正则匹配一组类名
    • 配合 variants 保留 hover、focus 等变体
  4. 4

    步骤4: 配置生产构建优化

    PostCSS 配置中添加 cssnano:

    • 开发环境不启用 cssnano(构建更快)
    • 生产环境自动启用 cssnano minify
    • 使用 process.env.NODE_ENV 控制启用条件
  5. 5

    步骤5: 配置服务器压缩

    Nginx 配置 Brotli 或 Gzip 压缩:

    • Brotli: brotli on; brotli_comp_level 6;
    • Gzip: gzip on; gzip_comp_level 6;
    • 均配置 text/css application/javascript 类型

常见问题

JIT 模式在 Tailwind v3 中如何启用?
Tailwind v3+ 默认启用 JIT 模式,无需额外配置。如果还在使用 Tailwind v2,需要在 tailwind.config.js 中添加 mode: 'jit'。
content 配置遗漏文件会导致什么问题?
遗漏文件会导致该文件中的 Tailwind 类名样式不生成,生产构建后样式丢失。改项目结构时务必同步更新 content 配置。
动态类名为什么会被 JIT 漏掉?
JIT 扫描的是源文件文本,动态拼接的类名如 `text-$&#123;color&#125;-500` 在源码中是模板字符串,不是完整类名,无法被检测到。需使用 safelist 或改用对象映射语法。
Netflix 的 6.5KB CSS 是原始体积还是压缩后?
6.5KB 是 Brotli 压缩后的网络传输体积。原始 CSS 文件在 PurgeCSS 和 cssnano 处理后约几十 KB,经 Brotli/Gzip 压缩后传输体积大幅减小。
Tailwind v4 的 Oxide 引擎有什么改进?
Oxide 引擎用 Rust 重写,增量构建速度提升 182 倍。原理是缓存文件扫描结果,只重新处理改动部分,大型项目构建从 10 秒级降到秒级。
safelist 配置过多会有什么影响?
safelist 会强制生成指定样式,配置过多会导致 CSS 文件膨胀。只在必要的动态类名场景使用,避免把所有可能用到的类名都加入。

参考资料

12 分钟阅读 · 发布于: 2026年3月30日 · 修改于: 2026年3月30日

评论

使用 GitHub 账号登录后即可评论

相关文章