如何从网站中删除未使用的 CSS?

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您的旅程每个阶段提供云产品。 立即开始使用 $200 免费信用额度!

我想事先让你知道:**这是一个难题。** 如果你来到这里是为了希望得到一个可以运行的工具,该工具可以告诉你从项目中删除哪些 CSS,那么…… 确实有这样的工具,但我警告你要非常小心使用它们,因为 没有一个工具可以告诉你完整的答案.

我知道你想要什么。 你想运行该工具,删除它告诉你的内容,然后在 2.2 分钟内获得一个更快的网站。 对不起,但我将让你失望。

我认为你应该对任何类似的工具保持健康的怀疑态度。 它们并不完全是在对你**撒谎**——它们通常只是没有足够的信息来提供安全且可操作的结果。 这并不是说你不能使用它们,或者无法做到。 让我们来一起看看。

动机

我想删除未使用的 CSS 的最主要原因是:

你使用了 CSS 框架(例如 Bootstrap),包含了框架的整个 CSS 文件,而你只使用了几种它提供的模式。

我能理解这一点。 CSS 框架通常不提供简单的方法来选择你正在使用的部分,而自定义源代码以使其正常工作可能需要你的团队不具备的专业知识水平。 这甚至可能是你一开始选择使用框架的原因。

假设你正在加载 100 KB 的 CSS。 我认为这太多了。(当我写这篇文章时,这个网站有大约 23 KB,而且有很多页面和模板。 我没有做任何特别的事情来减小体积。) 你怀疑或有一些证据表明,你没有使用其中一部分字节。 我能理解你的担忧。 如果你有一个 100 KB 的 JPG,你可以在某些工具中将其压缩为 20 KB,那很棒,非常值得。 **但是,对 CSS 执行此操作所带来的收益更为重要,因为 CSS 加载在头部,是阻止渲染的。** JPG 不是。

😬 查看“覆盖率”

Chrome 的 DevTools 有一个“覆盖率”选项卡,它会告诉你你的 CSS 和 JavaScript 中有多少在使用。 例如,如果我现在访问 CSS-Tricks 的主页……

它告诉我我的 style.css 文件中 70.7% 的 CSS 未使用。 我认为它是正确的,其余的 CSS 在其他地方使用。 我并没有将一个大型样式库转储到此网站上; 我手写了每一行代码,所以我怀疑超过三分之二的代码在全局范围内未使用。

我以为我可以开始“记录”,然后点击网站的不同区域,并观察未使用数字随着渲染不同的页面(具有不同的 HTML)而下降,但遗憾的是,当页面刷新时,覆盖率选项卡也会刷新。 在获得对 CSS 覆盖率的多页视图方面,它并不是很有用,除非你有一个单页应用程序,我猜是吗?

我不得不承认,我觉得查看代码覆盖率几乎没有用。 对我来说,它描绘了网站上所有这些未使用的代码的可怕画面,这会加剧我的怀疑,但我所能做的就是担心它。

这可能是让你产生需要发现并删除未使用的 CSS 的想法的原因。

我的主要担忧

我最担心的是,你会看到类似代码覆盖率这样的东西,并看到你未使用的行。

然后你会说,太棒了! 我会删除这些 CSS! 然后你真的这样做了,结果发现它根本就没有被使用,你却在整个网站上造成了严重的样式问题。 事情是这样的:除非你:

  1. 检查你整个网站的每个页面的覆盖率……
  2. 在执行所有 JavaScript 的同时……
  3. 在每个可能的组合状态下……
  4. 在每个可能的媒体查询组合中。

检查你的主页不算。 检查所有顶层页面不算。 你必须深入到每一个页面,包括不总是最容易想到的状态,更不用说所有边缘情况了。 否则,你可能会最终删除在禁用帐户的用户在宽限期内登录时出现的弹出模态中选择的信用卡下拉菜单的样式,并且这些用户也拥有要使用的礼品卡。

对于自动化工具来说,要承诺它们的方法完美无缺,这太复杂了,尤其是考虑到浏览器环境(不同的屏幕尺寸、不同的功能、不同的浏览器)和第三方的不确定性。

以下是我担心的一个例子。

PurifyCSS Online 会接收一些 URL 并立即提供一个可复制粘贴的 CSS 代码块供你使用

这是我将我的 css-tricks.com 放入 PurifyCSS Online 并获得新 CSS 的结果。

糟糕!

左边是正常的 CSS-Tricks。 右边是我应用了新的“净化”后的 CSS,它删除了许多其他页面所需的 CSS。

它给了我机会输入其他 URL(这很好),但 CSS-Tricks 上有数万个 URL。 其中许多非常相似,但它们都可能包含被使用的选择器。 我认为它没有执行 JavaScript,因为通过 JavaScript 进入页面的任何内容都没有被设置样式。 它甚至删除了我的 :hover 状态。

也许你已经明白为什么我对这些工具的信任如此之低了。

构建过程的一部分

PurifyCSS 可能更常被用作构建过程工具,而不是在线界面。 它们的文档包含 Grunt、Gulp 和 webpack 的说明。 例如,对要检查和处理的文件进行全局匹配

var content = ['**/src/js/*.js', '**/src/html/*.html'];
var css = ['**/src/css/*.css'];

var options = {
  // Will write purified CSS to this file.
  output: './dist/purified.css'
};

purify(content, css, options);

这为你提供了更多提高准确性的机会。 该内容块可以是构建网站的每个模板、部分和 JavaScript 文件的列表。 这可能需要花费一些时间来维护,但你肯定会获得更高的准确性。 它不考虑数据存储中的内容(例如,存储在数据库中的这篇博文)和第三方 JavaScript,但也许这对你来说并不重要,或者你可以通过其他方式解决它。

PurgeCSS 是 PurifyCSS 的竞争对手,它 警告 其比较技术

PurifyCSS 可以处理任何类型的文件,而不仅仅是 HTML 或 JavaScript。 PurifyCSS 的工作原理是查看你所有文件中的所有单词,并将它们与你 CSS 中的选择器进行比较。 每个单词都被视为一个选择器,这意味着许多选择器可能会被错误地认为是使用的。 例如,你可能在一段文字中碰巧有一个与你 CSS 中的选择器匹配的单词。

所以也要记住这一点。 它在比较潜在的选择器匹配方面很愚蠢,这既聪明又危险。

UnusedCSS 是一款在线服务,可以为你抓取你的网站

手动配置一个工具来从各个角度查看网站上的每个页面无疑是一件苦差事,而且随着代码库的发展,你需要每天保持同步。 有趣的是,在线服务 UnusedCSS 尝试通过根据你提供的单个 URL 抓取网站本身来克服这种负担。

我注册了**付费**服务,并将它指向 CSS-Tricks。 我承认,仅仅看一眼结果,我觉得它准确得多。

它告诉我我使用了 93% 的 CSS,这对我来说更符合实际,因为我是这个网站上所有 CSS 的手写者。

它还允许你下载清理后的文件,并提供很多自定义选项,例如,你可以选中或取消选中你实际上需要或不需要的选择器(例如,你看到它认为你不需要的类名,但你确信你确实需要它),以及添加前缀和删除重复选择器。

我喜欢在线抓取服务的准确性提高,**但我发现有很多噪音,而且我也无法看到如何将它实际纳入日常的构建和发布流程中**。

工具通常用于后期处理

假设您的 CSS 使用 Less 或 Sass 构建,然后使用后处理器将其编译成 CSS。您可能会在完成所有其他 CSS 预处理的最后阶段,加入自动清除未使用的 CSS 代码的操作。比如…

  1. Sass
  2. PostCSS / Autoprefixer
  3. [ 清除未使用的 CSS ]
  4. 生产环境 CSS

这种做法既有道理,又让我觉得有点好笑。您并没有真正 *修复* 生成未使用的 CSS 的样式。而是选择在构建结束时直接将其清除。我想 JavaScript 在很长一段时间里一直使用树状摇动来做类似的事情,所以这倒是有先例可循。但我仍然觉得很奇怪,因为 CSS 代码库是如此直接地上手操作。这种设置几乎鼓励您随意放置 CSS 代码,因为没有过度使用带来的惩罚。它消除了理解 CSS 应用和使用方式的任何动力。

PurgeCSS 是另一种工具,它接受明确的输入并为您提供结果

PurgeCSS 是未使用的 CSS 市场中的另一位玩家。我喜欢它的一点是,它 清楚地解释了它与其他工具的不同之处。例如,与 PurifyCSS 相比

PurifyCSS 最大的缺陷是缺乏模块化。但是,这也是它最大的优势。PurifyCSS 可以与任何文件类型一起使用,而不仅仅是 HTML 或 JavaScript。PurifyCSS 通过查看文件中的所有单词,并将它们与 CSS 中的选择器进行比较来工作。每个单词都被视为一个选择器,这意味着许多选择器可能会被错误地认定为已使用。例如,您可能在段落中有一个单词,它与 CSS 中的选择器匹配。

PurgeCSS 通过提供创建提取器的可能性来解决这个问题。提取器是一个函数,它接收文件的內容并提取其中使用的 CSS 选择器列表。它可以完美地移除未使用的 CSS 代码。

PurgeCSS 似乎是目前的主流工具。许多人正在使用它并撰写相关内容。

尽管 PurgeCSS 需要特殊配置才能与 Tailwind 配合使用,但 Tailwind 和 PurgeCSS 似乎是相得益彰的工具。实际上,它们的文档建议将它们一起使用,并提供 一个 CLI 用于在构建流程中使用它。

我认为其本质是:Tailwind 生成一个包含大量实用程序选择器的 CSS 文件。但他们并不希望你使用其中所有的内容。你可以在 HTML 中使用这些实用程序选择器来完成所有样式设置,然后使用 PurgeCSS 来查看所有的 HTML 代码,并从生产环境 CSS 中剔除未使用的实用程序选择器。

尽管如此,这仍然是一个持续的维护问题,你需要教它识别你网站上的所有模板(JavaScript、HTML 或其他),同时手动配置任何依赖于第三方资源的内容,并了解来自数据存储的任何数据可能无法在构建过程中进行查看,从而需要手动进行处理。

我最喜欢的技巧:让一个熟悉你的 CSS 代码库的人意识到这个问题,并努力逐步解决它

也许你觉得这像是一个落伍者需要跟上时代的做法,但是,嘿,对我来说,这只是最实际的做法。由于这个问题如此困难,我认为努力工作是解决问题的关键。它需要理解问题,并随着时间的推移,逐步朝着解决方案努力。一个深入参与你的前端开发的前端开发人员,经过一段时间后,会对 CSS 代码中哪些是已使用、哪些是未使用的有深入的了解,并能够将其缩减。

我见过一种极端的测试方法,它使用一个(例如 background-image: url(/is-this-being-used.gif?selector);)在 CSS 代码块中,然后随着时间的推移检查服务器日志,以查看该图像是否被访问。如果被访问,则表示它已被使用;如果没有,则表示它没有被使用。

但也许我最喜欢的工具是这个

视觉回归测试

您尽可能多地对您的网站进行截图 - 比如所有最重要的页面以及那些被操作成不同状态的页面 - 以及跨不同的浏览器和屏幕尺寸进行截图。这些截图是从 Git 的主分支中创建的。

然后,在任何分支合并到主分支之前,您需要获取所有这些分支的截图,并将它们与主分支中的截图进行比较。不是手动比较,而是通过程序进行比较。

这正是 Percy 所做的,所以请观看这个视频

多年来,人们一直尝试开发各种视觉回归测试工具,但 Percy 是我见过的唯一一个真正让我感到明了的工具。我不仅需要获取截图,还需要将它们进行比较,以便看到它们之间的视觉差异。我不仅想要看到差异,还需要批准或拒绝它们。我还希望这种批准能够阻止或允许合并,并且我希望能够在截图被获取之前控制浏览器。我不想手动更新比较图像。这些都是 Percy 的基本功能。

完整披露: Percy 之前曾赞助过 CSS-Tricks 上的一些内容 - 包括上面的视频 - 但不包括这篇帖子。

与原子 CSS 和 CSS-in-JS 的关系

我相信有很多读者会说:我不会有未使用的 CSS 代码,因为我使用的工具会生成它所需的 CSS 代码,不会多生成任何代码。

嘿,这很酷。

也许是 Atomizer。也许是 Tachyons,你还会通过 UnCSS 来运行它,并且你对它非常谨慎。也许是目前流行的 Tailwind + PurgeCSS 组合。

也许你用其他方式处理样式。如果你将 JavaScript 组件和样式紧密耦合在一起,比如 React 和 Emotion,甚至只是使用 CSS 模块,无论如何,减少未使用的 CSS 代码是 CSS-in-JS 的优势。由于树状摇动和代码拆分在许多基于 JavaScript 的构建流程中也随之而来,因此你不仅拥有更少的 CSS 代码,而且只会在需要时加载所需的内容。但是,这一切都存在权衡

如何在未来的项目中避免未使用的 CSS 代码?

我认为未来样式的发展趋势是将全局样式和组件化样式有意地分离。大多数样式都作用于组件,但也会做出一些全局的样式选择,这些选择充分利用了层叠样式表(例如全局的字体默认值)。

如果大多数样式都作用于组件,我认为未使用的样式积累的机会就会减少,因为你更容易理解一小块 HTML 和一小块 CSS,它们之间直接相关。当组件消失或进化时,相应的样式也会消失或进化。CSS 包是由实际使用的组件生成的。

CSS-in-JS 解决方案自然会朝着这个方向发展,因为样式会绑定到组件。这才是重点。但这并不是必需的。我喜欢 CSS 模块 的通用方法,它主要用于样式作用域,并且没有规定你必须使用特定的 JavaScript 框架。

如果这一切听起来很理论化或难以实现,而你只是有一个 Bootstrap 网站,你想要减小所有 Bootstrap CSS 代码的大小,我建议你从源代码中使用 Bootstrap,而不是使用最终默认分发的包。源代码是 SCSS,并从一系列高级包含文件中构建而成,所以如果你不需要 Bootstrap 的特定部分,你就可以删除它们。

在构建之前从 Bootstrap 中删除下拉菜单、徽章和面包屑。

祝各位好运!