PurgeCSS必须扫描编译后的JS产物而非.vue源文件,因其仅解析字符串字面量,无法展开变量;正确配置需指定dist/*.js路径、启用AST解析或使用vite-plugin-purgecss,并严格按postcss-import→tailwindcss→purgecss→autoprefixer→cssnano顺序执行。

content 必须扫编译后 JS,不是 .vue 源文件
PurgeCSS 默认读取 .vue 文件里的纯文本,比如 <div :class="`btn btn-${type}`">,它只看到 btn 和 btn-${type},根本不会展开变量。最终渲染出的 btn-primary 这类字符串,实际存在于 Webpack 或 Vite 编译后的 JS 产物中(例如 createElement('div', { class: 'btn primary-btn' }))。所以 content: ['src/**/*.vue'] 基本无效。
正确做法是让 PurgeCSS 扫描构建输出目录下的 JS 文件:
- Webpack 项目:用
path.join(__dirname, 'dist', '**', '*.js')(前提是关掉devtool: 'source-map',否则 source map 会污染匹配) - Vite 项目:改用
vite-plugin-purgecss,它在转换后钩子中运行,天然能拿到已处理的 class 字符串 - 更可靠方案:启用
modules: true(purgecss-webpack-plugin支持 AST 解析),可识别classNames(btn, type + '-btn')这类拼接逻辑
safelist 正则必须匹配最终 class 名,不是模板占位符
你写 /^btn-(primary|danger|success)$/,PurgeCSS 才会保留这三个;但漏了 btn-warning,它就没了。更关键的是 Tailwind 变体和属性选择器——它们是独立 class 名,不是修饰符:
-
hover:btn-primary和md:btn-primary是完整 class 字符串,需单独匹配:/^(?:hover|md|focus|group-hover):btn-(?:primary|danger|success)$/ -
[data-state=open]、[&_svg]这类语法默认提取器不认识,必须加/\[.*\]/和/\[&.*\]/ - 避免写
/btn-.*/:它会意外保留btn-clip(原生 CSS 属性)或btn-group(第三方库类),体积反弹
PostCSS 插件顺序错一位,CSS 就全空
构建后 CSS 文件只剩 @charset "UTF-8";,或 Lighthouse 显示 “CSS 规则数为 0”,基本就是顺序崩了。Tailwind 项目尤其敏感:
立即学习“前端免费学习笔记(深入)”;
- 必须严格按顺序:
postcss-import→tailwindcss→@fullhuman/postcss-purgecss→autoprefixer→cssnano -
@fullhuman/postcss-purgecss必须在autoprefixer后:否则.btn:-webkit-appearance这类带前缀的选择器无法被匹配 - 必须在
cssnano前:否则cssnano提前合并/重写选择器,PurgeCSS 再去删就找不到原始规则 -
cssnano配置里关掉重复清理:{ discardUnused: false }
Tailwind v3+ 用户别额外装 @fullhuman/postcss-purgecss
Tailwind v3+ 自带 JIT 编译器(即内置 Purge 功能),和 @fullhuman/postcss-purgecss 功能重叠且容易冲突。常见表现是:CSS 体积不降反升、部分变体失效、甚至报 No files found 错误。
正确做法是:
- 删掉
postcss.config.js里多余的@fullhuman/postcss-purgecss插件 - 确保
tailwind.config.js的content字段精准覆盖所有含 class 的文件:src/**/*.{js,ts,jsx,tsx,vue}(别漏.tsx或.jsx) - 动态类名仍需靠
safelist兜底,例如/^text-(blue|gray|red)-[0-9]+$/,不能依赖 JIT 自动推断
真正难的不是正则怎么写,而是分清哪些 class 是静态可扫描的、哪些必须靠 safelist 或 AST 解析来捕获——漏掉任何一类,上线后样式就丢得悄无声息。


















