purgecss 仅静态扫描字符串字面量,不执行 js 或解析模板表达式;content 配置遗漏文件、safelist 正则不匹配、postcss 插件顺序错误及 tailwind 内置冲突均导致误删。

它只扫描字符串字面量,不执行 JS 逻辑
PurgeCSS 的核心行为是静态文本扫描:它打开 ./src/**/*.tsx 这类文件,逐行提取引号内的 class 字符串,比如 className="btn-primary" 或 class="is-open"。但遇到 className={`btn-${type}`}、el.classList.add('hidden')、v-bind:class="['modal', isOpen && 'is-open']" 这类写法时,它根本不会运行 JS,也不会解析模板表达式——它只看到 `btn-${type}` 这个模板字面量,而 type 是变量,不是字符串,所以 btn-primary 从未作为完整字符串出现在源码中。
content 配置漏掉关键来源,等于没喂数据
误删的“动态类”往往来自你没显式纳入 content 的地方:
-
public/index.html里硬编码的class="swiper-slide"没加进数组,第三方轮播组件的类就丢了 -
import.meta.glob('./content/**/*.md')加载的 Markdown 文件含class="note warning",但content没配**/*.md - Vue 的
<style scoped></style>里用了@apply bg-blue-500,但 PurgeCSS 扫不到.vue文件里的<style></style>块(只扫<template></template>) - VitePress 或 Astro 中,MDX 组件的 class 写在 JSX 属性里,却只配了
**/*.ts,漏掉**/*.mdx
safelist 正则写错,匹配不上真实类名
即使知道要加白名单,也常因格式细节翻车:
- 写
/^text-/想保text-red-500,但 Tailwind 实际生成的是md:text-red-500或hover:text-red-500,得改成/^(hover|md|group-hover|data)-text-/ - 用字符串形式写
'card__header',但 HTML 里实际是class="card__header--sticky",这个修饰符没被覆盖 - BEM 类名带双下划线,正则写成
/card__[a-z]+/,但 JS 拼的是card__header--large,--large部分仍被删 - 第三方库如 Ant Design 的类带前缀
ant-btn,却只写了/^btn-/,漏掉ant-前缀
PostCSS 插件顺序或 cssnano 二次清理导致空规则
即便 content 和 safelist 都对了,构建输出还是空或错乱,大概率是管道崩了:
-
@fullhuman/postcss-purgecss放在cssnano后面,PurgeCSS 删完类,cssnano 又把已无选择器的声明块整个干掉,最终 CSS 仅剩@charset "UTF-8"; -
cssnano默认开启discardUnused: true,和 PurgeCSS 功能重叠,建议显式关掉:{ discardUnused: false } - 启用了
source-map(devtool: 'source-map'),map 文件内容混入 class 提取流程,导致匹配失效 - Tailwind v3+ 项目里还保留
@fullhuman/postcss-purgecss插件,和内置content配置冲突,造成重复扫描或跳过
className={user.role === 'admin' ? 'bg-red-500' : 'bg-gray-400'},如果 user.role 来自 API,那这两个类名永远不可能静态出现——safelist 不是补救措施,而是你对运行时行为的显式承诺。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











