☰
PurgeCSS 完整指南:基于 content 与 CSS 匹配,系统性移除未使用的样式
2026/9/26 2:35:10 网站建设 项目流程
  • 前端
  • 构建工具

【免费下载链接】purgecss

Remove unused CSS

项目地址:https://gitcode.com/gh_mirrors/pu/purgecss
点击查看免费下载

PurgeCSS 是当前仓库(monorepo)的核心项目,其使命只有一句话:Remove unused CSS(移除未使用的 CSS)。本指南以仓库根目录的 README.md 为骨架,完整讲解 PurgeCSS 的安装、编程式 API、CLI、配置选项、Safelisting 与 Extractors 机制,并结合 packages/purgecss/src/index.ts 等源码揭示底层实现原理。读完本文,你将掌握如何在任何构建流程中接入 PurgeCSS,把最终交付的 CSS 体积压到最小,同时避免误删仍在使用中的样式。

PurgeCSS 是什么:问题与解决方案

在构建网站时,绝大多数项目都会引入 CSS 框架,例如 Bootstrap、Materializecss、Foundation 等。但实际项目中往往只用到框架的一小部分能力,大量未被使用的 CSS 样式会一并打包进最终产物,白白增加页面体积、拖慢加载速度。

PurgeCSS 正是为解决这一问题而生。它的工作方式非常直观:

  1. 分析内容文件(content):读取 HTML、JS、Vue、Pug 等文件,提取其中实际出现的 CSS 选择器(标签、类名、ID、属性名等);
  2. 分析 CSS 文件(css):解析需要清理的样式表;
  3. 匹配与清除:把 CSS 中的选择器与内容文件中出现的选择器做匹配,凡是内容中没有出现的选择器,对应的规则就会被移除,最终输出更小的 CSS 文件。

整个过程可以在构建阶段自动完成,无需人工维护"哪些样式在用"的清单。从源码看,这一匹配逻辑集中在PurgeCSS类的shouldKeepSelector()方法中(packages/purgecss/src/index.ts):它对每个 CSS 选择器逐一判断,只有出现在提取结果集(ExtractorResultSets)中的 class、id、tag、attribute 才会被保留。

安装与快速上手

安装

把 PurgeCSS 安装为开发依赖即可:

npm install purgecss --save-dev

安装完成后,即可在 JavaScript / TypeScript 项目中直接调用其编程式 API。

基础用法

README 给出了最小可用的调用示例:通过PurgeCSS类的purge()方法,传入content(内容文件 glob)和css(CSS 文件 glob),purge()返回清理后的结果数组。

import { PurgeCSS } from "purgecss"; const purgeCSSResults = await new PurgeCSS().purge({ content: ["**/*.html"], css: ["**/*.css"], });

这是 ES Module 的写法;如果使用 CommonJS,则等价于:

const { PurgeCSS } = require("purgecss"); const purgeCSSResult = await new PurgeCSS().purge({ content: ["**/*.html"], css: ["**/*.css"], });

purge()返回的purgeCSSResults是一个数组,每个元素对应一份被清理的 CSS 文件,形如:

[ { file: "main.css", css: "/* purged css for main.css */", }, { file: "animate.css", css: "/* purged css for animate.css */", }, ];

对应的类型定义是ResultPurge(见 docs/api.md):

interface ResultPurge { css: string; file?: string; rejected?: string[]; rejectedCss?: string; }

当启用了rejected或rejectedCss选项时,结果中还会额外携带被移除选择器或整段被移除 CSS 的信息。

核心工作流程:从文件到净化后的 CSS

purge()的内部调用链可以从源码中得到完整印证(packages/purgecss/src/index.ts):

  1. 合并选项:将用户传入的选项与defaultOptions合并,safelist会被standardizeSafelist()标准化为统一结构;
  2. 提取文件型内容:调用extractSelectorsFromFiles(),对content中的文件路径/glob 逐一读取并用对应的 extractor 提取选择器;
  3. 提取原始字符串内容:调用extractSelectorsFromString(),处理content中形如{ raw, extension }的原始字符串;
  4. 合并提取结果:通过mergeExtractorSelectors()把两类提取结果合并为一个ExtractorResultSets;
  5. 遍历 CSS:调用getPurgedCSS(),对每份 CSS 用 PostCSS 解析成 AST,再经walkThroughCSS()逐节点评估、删除未使用规则,最后按需执行removeUnusedFontFaces()、removeUnusedKeyframes()、removeUnusedCSSVariables()三项收尾清理。

其中walkThroughCSS()(packages/purgecss/src/index.ts)是核心遍历器:对rule节点调用evaluateRule()做选择器级评估,对atrule节点调用evaluateAtRule()登记 keyframes / font-face,对comment节点识别purgecss start ignore/purgecss end ignore注释以切换忽略状态。

值得注意的细节是evaluateRule()中对:where()与:is()的处理:第一轮遍历会移除其中未使用的选择器,但保留伪类本身;第二轮再移除已被清空、只剩空:where/:is的选择器(packages/purgecss/src/index.ts)。同时,空规则(无选择器或无数值)会被自动删除,避免产出残留的空壳。

配置选项详解

完整、可复制的配置示例与类型定义位于 docs/configuration.md。defaultOptions的实际默认值可以在 packages/purgecss/src/options.ts 中查到:css、content、extractors、safelist、blocklist、skippedContentGlobs、dynamicAttributes默认为空数组/空对象,fontFace、keyframes、rejected、rejectedCss、sourceMap、stdin、stdout、variables默认为false,defaultExtractor默认匹配所有由字母、数字、下划线、连字符组成的词。

content:指定要分析的内容文件

content接收一个由文件名或 glob 模式组成的数组,文件可以是 HTML、Pug、Blade、JS、Vue 等任何可能包含选择器的文件:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], });

除了文件路径,PurgeCSS 也支持原始内容:传入带raw属性的对象即可。若希望自定义 extractor 能正确匹配,还需同时传入extension属性:

await new PurgeCSS().purge({ content: [ { raw: '<html><body><div class="app"></div></body></html>', extension: "html", }, "**/*.js", "**/*.html", "**/*.vue", ], css: [ { raw: "body { margin: 0 }", }, "css/app.css", ], });

从extractSelectorsFromFiles()的实现(packages/purgecss/src/index.ts)可以看到,PurgeCSS 会先尝试把条目当作真实文件直接读取,失败后再用glob.sync展开 glob;若 glob 展开后仍没有任何文件,会输出一条警告:"No files found from the passed PurgeCSS option 'content'.",方便排查路径写错的问题。

css:指定要清理的样式表

与content类似,css同样接收文件名或 glob 数组,也支持{ raw: "..." }形式的原始 CSS:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], });
await new PurgeCSS().purge({ content: [ { raw: '<html><body><div class="app"></div></body></html>', extension: "html", }, ], css: [ { raw: "body { margin: 0 }", }, ], });

在getPurgedCSS()(packages/purgecss/src/index.ts)中,字符串形式的 css 条目同样先经 glob 展开,再按文件读取(除非启用stdin);每条 CSS 都会独立产出{ css, file }结果,并支持sourceMap、rejected、rejectedCss等附加字段。

defaultExtractor:为所有文件设置默认提取器

如果发现大量未使用的 CSS 未被移除(即提取不够充分),可以为所有类型的文件统一指定一个自定义 extractor:

await new PurgeCSS().purge({ // ... defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [], });

defaultOptions中的默认 extractor 是content.match(/[A-Za-z0-9_-]+/g) || [],它把文件中的每个"词"都当作潜在选择器,但不会识别@、:、/等特殊字符(详见 docs/extractors.md 对默认 extractor 局限性的说明)。当getFileExtractor()找不到与文件扩展名匹配的 extractor 时,就会回退到defaultExtractor(packages/purgecss/src/index.ts)。

extractors:按文件扩展名精确提取

如果希望不同扩展名使用不同 extractor,可以用extractors选项按扩展名注册:

import purgeFromHTML from "purge-from-html"; await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], extractors: [ { extractor: purgeFromHTML, extensions: ["html"], }, { extractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [], extensions: ["vue", "js"], }, ], });

getFileExtractor()会按文件后缀名匹配extractors数组中第一个命中项。官方文档建议把 extractor 视为一种高级优化手段:它们能带来更好的准确率,但行为因实现而异,可能更难推理,并非所有项目都需要。更多内容见 docs/extractors.md。

fontFace(默认 false)

若 CSS 中存在未被使用的@font-face规则,可开启此选项移除它们:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], fontFace: true, });

源码层面,evaluateAtRule()会收集所有@font-face中的font-family名称(packages/purgecss/src/index.ts),collectDeclarationsData()则收集实际用到的font-family值,最后removeUnusedFontFaces()删除未被引用的规则(packages/purgecss/src/index.ts)。@font-face的收集与清理测试可见 packages/purgecss/tests/font-faces.test.ts。

keyframes(默认 false)

如果使用了 animate.css 这类动画库,可以开启此选项移除未使用的 keyframes:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], keyframes: true, });

实现上,collectDeclarationsData()会从animation/animation-name声明中收集动画名(packages/purgecss/src/index.ts),evaluateAtRule()把以keyframes结尾的 at-rule 登记为待清理对象,removeUnusedKeyframes()最终删除未被引用、且未被safelist.keyframes放行的 keyframes(packages/purgecss/src/index.ts)。

variables(默认 false)

如果项目使用 CSS 自定义属性(Custom Properties,即 CSS 变量),或使用了 Bootstrap 这类依赖 CSS 变量的库,可开启此选项移除未使用的 CSS 变量:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], variables: true, });

实现由VariablesStructure类承担(packages/purgecss/src/VariablesStructure.ts):它记录每个--*变量的定义位置与所有var(...)引用,removeUnusedCSSVariables()调用variablesStructure.removeUnused()完成清理。相关测试见 packages/purgecss/tests/css-variables.test.ts。

rejected(默认 false)与 rejectedCss(默认 false)

调试时扫描被移除的列表,往往能快速发现误删或异常:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], rejected: true, });

开启后,结果对象的rejected字段会包含所有被移除的选择器(selectorsRemoved集合,见 packages/purgecss/src/index.ts)。对应的测试用例见 packages/purgecss/tests/rejected.test.ts。

如果希望保留被丢弃的整段 CSS 用于审计或双版本发布,则用rejectedCss:

await new PurgeCSS().purge({ content: ["index.html", "**/*.js", "**/*.html", "**/*.vue"], css: ["css/app.css"], rejectedCss: true, });

开启后,evaluateRule()会把每条规则中被移除的选择器重组为克隆规则并存入removedNodes(packages/purgecss/src/index.ts),最终通过rejectedCss字段以整段 CSS 字符串形式返回(packages/purgecss/src/index.ts)。测试见 packages/purgecss/tests/rejectedCss.test.ts。

safelist:指定必须保留的选择器

safelist用于声明哪些选择器是安全的、必须留在最终 CSS 中。它有两种形式。

简单形式——字符串与正则的数组:

safelist: ["random", "yep", "button", /^nav-/];

复杂形式——对象结构:

safelist: { standard: ["random", "yep", "button", /^nav-/], deep: [], greedy: [], keyframes: [], variables: [] }

例如:

const purgecss = await new PurgeCSS().purge({ content: [], css: [], safelist: ["random", "yep", "button"], });

此时.random、#yep、button三个选择器会留在最终 CSS 中(注意字符串会同时匹配 class、id 与标签名三种形态)。再如:

const purgecss = await new PurgeCSS().purge({ content: [], css: [], safelist: [/red$/], });

所有以red结尾的选择器(如.bg-red)都会被保留。

safelist在源码中的标准化逻辑由standardizeSafelist()完成:数组形式会被展开为{ standard: [...] }并补齐其他字段的默认值;对象形式则与默认结构做浅合并(packages/purgecss/src/index.ts)。此外,isSelectorSafelisted()还会检查一份内部安全名单CSS_SAFELIST(packages/purgecss/src/internal-safelist.ts)以及::开头的伪元素,这些会被默认保留。

safelist.deep:连同子选择器一起保留

safelist.deep按正则匹配选择器,并同时保留其所有后代选择器:

const purgecss = await new PurgeCSS().purge({ content: [], css: [], safelist: { deep: [/red$/], }, });

此例中,即使child-of-bg在内容中从未出现,.bg-red .child-of-bg也会被完整保留。实现对应isSelectorSafelistedDeep()(packages/purgecss/src/index.ts):只要某个选择器片段命中deep正则,整条规则立即保留。

safelist.greedy:任意片段命中即整条保留

safelist.greedy更激进:只要选择器的任意一部分命中正则,整条选择器就保留:

const purgecss = await new PurgeCSS().purge({ content: [], css: [], safelist: { greedy: [/red$/], }, });

例如button.bg-red.nonexistent-class,即使button与nonexistent-class都未在内容中出现,也会被保留。实现对应isSelectorSafelistedGreedy(),它会在shouldKeepSelector()里把选择器拆成多个片段逐一比对(packages/purgecss/src/index.ts、packages/purgecss/src/index.ts)。相关测试覆盖了 children 与 greedy 两种模式,见 packages/purgecss/tests/safelist.test.ts。

blocklist:强制移除选择器

blocklist与 safelist 相反:即使某个选择器被 extractor 判定为"已使用",只要命中 blocklist,仍会被强制移除:

blocklist: ["usedClass", /^nav-/];

即使nav-links和usedClass都被 extractor 提取到,它们仍会被删除。对应isSelectorBlocklisted()(packages/purgecss/src/index.ts),且 blocklist 的优先级高于保留判断——在shouldKeepSelector()中,命中 blocklist 的选择器会直接返回false。测试见 packages/purgecss/tests/safelist.test.ts(blocklist 用例)。

skippedContentGlobs:跳过不需要扫描的文件

当content使用 glob 时,可以用skippedContentGlobs排除某些文件或目录:

skippedContentGlobs: ["node_modules/**", "components/**"];

此时 PurgeCSS 不会扫描node_modules与components两个目录。注意:当content不是 glob(而是具体文件路径)时,此选项不生效。源码在 glob 展开阶段直接把这些模式透传给glob.sync的ignore参数(packages/purgecss/src/index.ts)。

dynamicAttributes:自定义动态属性

用于补充aria-selected、data-selected这类自定义动态属性选择器:

dynamicAttributes: ["aria-selected"];

实现上,属性选择器在匹配时会额外检查dynamicAttributes列表,以及value、checked、selected、open这四个默认"动态"属性——因为它们依赖用户交互状态,无法从静态内容中可靠推断,所以默认总是保留(packages/purgecss/src/index.ts)。

配置文件:purgecss.config.js

除了直接传选项对象,PurgeCSS 也支持读取配置文件。默认配置文件名是purgecss.config.js(常量CONFIG_FILENAME,见 packages/purgecss/src/constants.ts),它是一个普通的 JavaScript 文件:

module.exports = { content: ["index.html"], css: ["style.css"], };

然后可以在代码中以两种方式使用:

const purgecss = await new PurgeCSS().purge(); // 或把配置文件路径作为唯一参数传入 const purgecss = await new PurgeCSS().purge("./purgecss.config.js");

setOptions()(packages/purgecss/src/index.ts)会基于process.cwd()解析并动态import配置文件,加载失败时抛出带有 "Error loading the config file" 前缀的错误(错误常量见 packages/purgecss/src/constants.ts)。CLI 的--config选项走的也是同一套加载逻辑。

命令行界面(CLI)

PurgeCSS 同时提供 CLI,既可直接使用,也可搭配配置文件使用。先安装(全局或作为 devDependency 后配合npx均可):

npm i -g purgecss

运行purgecss --help可查看全部选项:

Usage: purgecss --css <css...> --content <content...> [options] Remove unused css selectors Options: -V, --version output the version number -con, --content <files...> glob of content files -css, --css <files...> glob of css files -c, --config <path> path to the configuration file -o, --output <path> file path directory to write purged css files to -font, --font-face option to remove unused font-faces -keyframes, --keyframes option to remove unused keyframes -v, --variables option to remove unused variables -rejected, --rejected option to output rejected selectors -rejected-css, --rejected-css option to output rejected css -s, --safelist <list...> list of classes that should not be removed -b, --blocklist <list...> list of selectors that should be removed -k, --skippedContentGlobs <list...> list of glob patterns for folders/files that should not be scanned -h, --help display help for command

CLI 选项与配置文件选项一一对应(CLI 入口源码见 packages/purgecss/src/bin.ts,基于commander解析参数)。

--css

purgecss --css css/app.css css/palette.css --content src/index.html

--content

--content接受多个文件名或 glob 模式,文件类型不限(HTML、Pug、Blade 等):

purgecss --css css/app.css --content src/index.html src/**/*.js

--config

使用配置文件时,用-c/--config指定路径:

purgecss --config ./purgecss.config.js

--output

默认情况下 CLI 把结果输出到控制台;需要落盘时用--output指定输出目录:

purgecss --css css/app.css --content src/index.html "src/**/*.js" --output build/css/

--safelist

防止某个选择器被移除时,把它加入 safelist:

purgecss --css css/app.css --content src/index.html --safelist classnameToSafelist

更完整的 CLI 用法与输出文件测试见 packages/purgecss/tests/cli 下的测试文件,包括控制台输出(cli-console-output.test.ts)、单文件输出(cli-file-output.test.ts)、多文件输出(cli-multiple-files-output.test.ts)与选项解析(cli-options.test.ts)。

在 CSS 中直接使用忽略注释(Safelisting 进阶)

除了配置项,还可以在 CSS 源码中通过特殊注释直接控制保留行为(详见 docs/safelisting.md)。使用/* purgecss ignore */保留下一条规则:

/* purgecss ignore */ h1 { color: blue; }

使用/* purgecss ignore current */保留当前规则(注释写在规则内部):

h1 { /* purgecss ignore current */ color: blue; }

使用/* purgecss start ignore */与/* purgecss end ignore */保留一段连续范围:

/* purgecss start ignore */ h1 { color: blue; } h3 { color: green; } /* purgecss end ignore */ h4 { color: purple; }

这些注释对应的常量与识别逻辑分别在 packages/purgecss/src/constants.ts 与isIgnoreAnnotation()、hasIgnoreAnnotation()(packages/purgecss/src/index.ts)中:purgecss ignore(下一条)、purgecss ignore current(当前规则内部)、purgecss start ignore/purgecss end ignore(范围开关)。注释本身在处理完成后会被从输出中移除。

一个重要的坑(Gotchas):PostCSS、cssnano 等 CSS 优化工具在构建流程中可能先于 PurgeCSS 剥离注释,导致忽略注释失效。由于这些步骤在开发模式下常常被跳过,问题容易被忽视。解决办法是用感叹号把注释标记为重要注释:

/*! purgecss start ignore */ h5 { color: pink; } h6 { color: lightcoral; } /*! purgecss end ignore */

这样压缩工具会保留它们,PurgeCSS 仍能识别。

Extractors:提取器的机制与扩展

PurgeCSS 依赖 extractor 从内容文件中获取"使用了哪些选择器"。HTML、Pug、JS 等不同类型的文件都可能包含选择器,因此提取器需要按文件类型适配(详见 docs/extractors.md)。

默认 extractor适用于所有文件类型,但能力有限:它把文件中的每个词都视为选择器,不识别@、:、/等特殊字符,在复杂模板场景下可能提取不充分。

自定义 extractor就是一个普通函数:接收文件内容字符串,返回选择器数组(tags、classes、ids),或返回结构更精细的对象:

interface ExtractorResultDetailed { attributes: { names: string[]; values: string[]; }; classes: string[]; ids: string[]; tags: string[]; undetermined: string[]; }
const purgeFromJs = (content) => { // 返回 css selector 数组 };

返回详细对象能让 PurgeCSS 获得更好的匹配精度:在ExtractorResultSets(packages/purgecss/src/ExtractorResultSets.ts)中,这些结果被拆分到attrNames、attrValues、classes、ids、tags、undetermined六个集合里,分别支撑属性名、属性值、类、ID、标签的精确匹配;无法归类的词进入undetermined,在各类匹配时兜底参与判断(例如hasClass()会同时检查classes与undetermined)。

在配置中使用 extractor 的完整示例:

import { purgeCSSFromPug } from "purgecss-from-pug"; import { purgeCSSFromHtml } from "purgecss-from-html"; const options = { content: [], // 用于提取选择器的文件 css: [], // css extractors: [ { extractor: purgeCSSFromPug, extensions: ["pug"], }, { extractor: purgeCSSFromHtml, extensions: ["html"], }, ], }; export default options;

约定俗成的命名规则是purgecss-from-[文件类型](如purgecss-from-pug),便于在 npm 上搜索同类提取器。仓库中已经实现并发布了多个提取器包:purgecss-from-html(packages/purgecss-from-html)、purgecss-from-jsx(packages/purgecss-from-jsx)、purgecss-from-tsx(packages/purgecss-from-tsx)、purgecss-from-pug(packages/purgecss-from-pug)。官方文档提示这些提取器仍处于演进阶段,生产环境使用前需自行评估。

生态:monorepo 中的包与插件

当前仓库是一个用 Lerna 管理的 monorepo(见根目录 lerna.json),多个包从同一份代码库发布到 npm。README 列出的核心包如下(各包详情可从 packages 目录进入):

Package说明
purgecssPurgeCSS 的核心包,包含分析文件、移除未使用 CSS 的核心方法
postcss-purgecss面向 PostCSS 的 PurgeCSS 插件
purgecss-webpack-plugin面向 Webpack 的 PurgeCSS 插件
gulp-purgecss面向 Gulp 的 PurgeCSS 插件
grunt-purgecss面向 Grunt 的 PurgeCSS 插件
rollup-plugin-purgecss面向 Rollup 的 PurgeCSS 插件
purgecss-from-htmlHTML 提取器
purgecss-from-pugPug 提取器
purgecss-with-wordpress面向 WordPress 的 safelist 集合
vue-cli-plugin-purgecssVue CLI 插件

绝大多数构建工具与框架都在使用 PostCSS,因此最快上手 PurgeCSS 的方式是使用其 PostCSS 插件(docs/plugins/postcss.md):

npm i -D @fullhuman/postcss-purgecss
const purgecss = require("@fullhuman/postcss-purgecss"); module.exports = { plugins: [ purgecss({ content: ["./**/*.html"], }), ], };

仓库还提供了面向各主流框架的接入指南,可作为对应生态下的实战参考:

  • 前端框架:Vue.js、Nuxt.js、React.js、Next.js、Razzle
  • 静态站点与 CMS:Hugo、WordPress
  • 构建工具插件:Webpack、Gulp、Grunt、Gatsby

进一步阅读

  • 配置选项全解:全部选项、类型定义与可复制示例
  • 命令行界面:CLI 安装、参数与输出
  • 编程式 API:ES Module / CommonJS 两种用法与返回结构
  • Safelisting:safelist 各种形态与 CSS 内注释的完整说明
  • Extractors:默认提取器局限、自定义提取器与结果结构
  • Comparison:与其他同类工具/方案的对比
  • 核心实现:packages/purgecss/src/index.ts 中的PurgeCSS类、shouldKeepSelector()、walkThroughCSS(),以及 packages/purgecss/src/ExtractorResultSets.ts 中的选择器结果集
  • 前端
  • 构建工具

【免费下载链接】purgecss

Remove unused CSS

项目地址:https://gitcode.com/gh_mirrors/pu/purgecss
点击查看免费下载
上一篇:DeepSeek-Math 完整使用指南:10个快速上手技巧 🚀
下一篇:如何让老旧Mac焕发新生:OpenCore Legacy Patcher完全解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询