PurgeCSS 默认只清理编译后 JS 中可见的类名,无法识别运行时拼接的 class;必须扫描构建产物而非源码,插件顺序需在 autoprefixer 后、cssnano 前,并配置正则保 Tailwind 变体和属性选择器。

<PurgeCSS 默认不“清理冗余”,它只删它“看得到”的类——而你项目里 80% 的 class 是运行时拼出来的,比如 className={<code>modal ${isOpen ? 'is-open' : ''}、hover:bg-blue-500、[data-state=open]。直接配 content 扫 .vue 或 .tsx 源文件,基本等于白配。
content 必须扫编译后 JS,不是源码
PurgeCSSPlugin 在 Webpack 中默认读取原始文件,但 Vue 模板里写class="btn {{ type }}-btn"、React 里写 className="btn " + type + "-btn",这些字符串根本不会出现在源码里,而是由 vue-loader 或 Babel 编译后注入到 JS 产物中(比如 createElement('div', { class: 'btn primary-btn' }))。
- 别用
glob.sync('./src/*<em>/</em>.vue'):Windows 路径分隔符错乱,且提取不到真实类名 - 改用绝对路径扫描构建产物:
path.join(__dirname, 'dist', '*<em>', '</em>.js')(前提是关掉devtool: 'source-map',否则 source map 会污染类名匹配) - 更可靠方案:启用
modules: true(purgecss-webpack-plugin支持 AST 解析),它能识别classNames(btn, type + '-btn')这类动态拼接逻辑
PostCSS 插件顺序错一位,CSS 就全空
构建后 CSS 文件只剩@charset "UTF-8";,或 Lighthouse 显示 “CSS 规则数为 0”,基本就是插件顺序崩了。
-
@fullhuman/postcss-purgecss必须在cssnano之前:否则cssnano提前合并/重写选择器,PurgeCSS 再去删就找不到原始规则 - 它也必须在
autoprefixer之后:否则带前缀的选择器(如.btn:-webkit-appearance)无法被匹配 - 正确顺序:
postcss-import→tailwindcss(如有)→@fullhuman/postcss-purgecss→autoprefixer→cssnano -
cssnano配置里必须关掉重复清理:discardUnused: { discardUnused: false }
safelist 不加正则,Tailwind 变体和属性选择器必丢
group-hover:text-red-500 失效、[data-state=open] 样式消失、[&_svg] 图标变空白——都是默认提取器不认识这些模式。
- 加正则保 Tailwind 变体:
/^(hover|focus|group-hover|data)-/ - 加正则保属性选择器:
/[.*]/ - 启用
keyframes: true和fontFace: true,否则@keyframes和@font-face可能被连带清除 - 对于
[&_svg]这类嵌套语法,要么自定义defaultExtractor,要么迁移到 Tailwind v3+ 内置的content配置(推荐)
Tailwind 项目别额外装 @fullhuman/postcss-purgecss
Tailwind v3+ 自带 Purge 功能,和@fullhuman/postcss-purgecss 冲突。多装一个,轻则重复清理,重则把整个 CSS 清空。
- 删除
postcss.config.js里多余的@fullhuman/postcss-purgecss插件 - 把所有清理逻辑收归
tailwind.config.js的content字段(支持数组、glob、函数返回路径) - 若用了
vite-plugin-css-injected-by-js等 JS 注入 CSS 的插件,它的输出不在content范围内,得手动加进数组,或禁用该插件
真正难的不是“怎么配”,是搞清 PurgeCSS 看不见什么、为什么看不见、以及哪些地方它根本没权限看见。


















