PurgeCSS删掉已用class的根本原因是它仅识别静态字符串字面量,无法处理动态拼接、JS注入或模板字符串中的类名;必须通过safelist显式声明动态类模式或确保源码中完整出现。

为什么PurgeCSS会删掉你明明用到的 class
PurgeCSS 不是误判,而是根本没“看见”那些类名。它只扫描源码中静态出现的字符串字面量,比如 class="btn active" 或 className="text-red-500";对 className={`btn-${type}`}、el.classList.add('hidden')、甚至 :class="['icon-' + name]" 这类运行时拼接或 JS 注入的写法,它完全不处理——不是不想,是做不到。
动态类名必须显式加到 safelist 才安全
别指望 PurgeCSS 自动推导模板字符串结果。最可靠的方式是把已知模式直接写进 safelist,它优先级最高,且不依赖文件扫描路径是否覆盖到位。
- 用正则匹配固定前缀:
/^text-(red|blue|green)-[0-9]{3}$/保留下划线色值类 - 用 glob 字符串通配响应式变体:
"md:text-*"、"hover:opacity-*" - 函数式写法更可控:
{ pattern: /^(?:md|hover):text-(?:red|blue)-(?:500|600)$/ } - 避免
/text-.*/这种宽泛正则——它可能意外保留text-clip这类原生属性
PostCSS 插件里 content 路径再全也没用
很多人以为只要 content: ['./src/**/*.{js,jsx,ts,vue}'] 就万事大吉,但问题常出在:Vue 单文件组件里的 <div :class="xxx">、React 中的 JSX 模板字符串,原生提取器根本不会解析 AST,只当普通文本扫一遍,变量部分直接被跳过。<ul>
<li>Webpack 场景下,<code>purgecss-webpack-plugin 扫的是原始 .vue 文件,不是编译后的 JS,所以 class="btn-{{ type }}" 这种写法压根不会被识别
vite-plugin-purgecss 默认不处理 import.meta.glob 动态导入的模块,这些模块里的 class 字符串得手动加进 content 数组<mybutton class="custom-btn"></mybutton>)若来自 props 或 JSON 配置,也必须进 safelist
验证到底删了哪些 class,别靠猜
构建后样式消失,第一反应不该是改配置,而是确认 PurgeCSS 究竟干了什么。
- 在
tailwind.config.js加debug: true(v3.3+),构建日志会列出preserved和discarded的类名 - 临时注释掉
content字段再 build 一次——如果样式全回来了,就是 100% purge 误删 - 用 CLI 单独测试:
npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify --content "./src/**/*.{js,ts,jsx,tsx}",绕过构建工具封装干扰
真正容易被忽略的点是:PurgeCSS 匹配的是你写的源码字符串,不是浏览器最终渲染出的 DOM class。哪怕你在控制台看到 class="bg-blue-500",只要这串字符没以完整形式出现在某个 .js 或 .vue 文件里,它就大概率被清掉。











