PurgeCSS 误删动态类名是因为它只扫描源码中静态字符串,不执行JS或解析模板逻辑;需通过精准的safelist正则(如/btn-(primary|danger)/)兜底,同时确保content配置覆盖所有含动态类的文件路径。

PurgeCSS 误删动态类名,不是它坏了,而是它根本没看到那些类——它只认源码里“写死”的字符串,不执行 JS,也不解析模板绑定逻辑。
为什么 class="btn btn-${type}" 里的 btn-primary 被删了
PurgeCSS 扫描的是 .vue 或 .tsx 文件里的原始文本,btn-${type} 这种模板字面量不会被展开。它只提取出 btn 和 btn-${type} 两个字符串,而 btn-primary 在运行时才拼出来,压根不在扫描结果里。
- Vue 的
:class="['btn', `btn-${type}`]"、React 的className={`btn ${type}-btn`}全部同理 - 哪怕
type = 'primary'写在同一个文件里,PurgeCSS 也不会做变量求值 - 第三方库组件(如
<AntdButton type="primary">)内部生成的ant-btn-primary同样不可见
怎么让 btn-primary 这类动态类名不被删
必须靠 safelist 主动兜底,不能指望自动识别。关键不是“加得够多”,而是“匹配得准”。
- 用函数式 pattern 更可控:
{ pattern: /btn-(primary|danger|warning)/ },避免宽泛的/btn-.*/意外保留btn-group或btn-clip - 变体前缀(
hover:btn-primary、md:btn-primary)是独立字符串,需显式覆盖:/^(hover|md|group-hover):btn-(primary|danger)$/ - 非捕获组必须写成
(?:hover|md),写成(hover|md)会导致正则语法错误,整个 pattern 失效 - 如果
type来自 JSON 配置或 CMS 字段(如item.class = "btn-primary"),这个字符串也得进safelist
content 配置漏掉路径,safelist 再全也没用
safelist 是兜底,content 是基础。PurgeCSS 只会从 content 列出的文件里提取类名,再和 safelist 合并保留。漏掉一个路径,等于主动放弃那部分类名的扫描权。
立即学习“前端免费学习笔记(深入)”;
- 别只写
src/**/*.vue——setup()里返回的 class 字符串可能在composables/useButton.ts里,必须加进content - Windows 下
glob.sync('./src/**/*.vue')易因路径分隔符失效,改用path.join(__dirname, 'src', '**', '*.{js,ts,vue}') - 异步路由组件(
defineAsyncComponent(() => import('./Foo.vue')))的路径若未被 glob 捕获,其 class 也不会进入扫描范围
真正容易被忽略的点:PurgeCSS 不处理 import 进来的第三方 CSS,也不解析 @apply 或 clsx() 的调用链。它只忠实地比对“你写的字符串”和“CSS 文件里的选择器”。动态逻辑越重,safelist 就越不能省,但也不能乱加——每条正则都得对应真实渲染出的类名,否则只是徒增体积。


















