Font Awesome 是一个非常受欢迎的图标库。不幸的是,它很容易以一种导致性能不佳的方式使用。通过对 Font Awesome 进行子集化,我们可以从它提供的字体文件中删除任何未使用的字形。这将减少通过网络传输的字节数,并提高性能。
让我们在一个 Font Awesome 项目中一起对字体进行子集化,看看它会产生什么影响。在进行的过程中,我假设您正在导入 Font Awesome 提供的 CSS 文件,并使用其网络字体来显示图标。
让我们开始设置
为了演示,我只有一个 HTML 文件,它导入 Font Awesome 的基础 CSS 文件。为了获得合理的图标样本,我列出了我在其中一个副项目中使用的每个图标。
这是在对字体进行子集化之前,我们的 HTML 文件在浏览器中的样子

这是 DevTool 的 Network 选项卡,可以看到正在下载的内容。

现在让我们看看我们的字体文件渲染所有这些内容需要多少字节。
这是我们的基本情况
我们想看看 Font Awesome 最直接、性能最差的使用方式是什么样的。换句话说,我们想要实现最慢的可能实现,没有任何优化。我正在导入 Font Awesome 提供的 all.min.css
文件。
如上所述,压缩后的文件大小为 33.4KB,这完全没问题。不幸的是,当我们查看 DevTool 的 Font 选项卡时,情况变得更糟了。

虽然字体文件不像 JavaScript 那样会给您的浏览器带来负担,但它们仍然是浏览器需要下载的字节,仅仅是为了几个小图标。考虑到您的一些用户可能在移动设备上浏览您的网站,远离强大的或快速的互联网连接。
使用 PurifyCSS 的第一次尝试
Font Awesome 的主要样式表包含了数千个图标的定义。但如果我们最多只需要几十个呢?当然我们可以删除不需要的东西吗?
有很多工具可以分析您的代码,并 从样式表中删除未使用的样式。我碰巧正在使用 PurifyCSS。虽然这个库不再积极维护,但思路是一样的,最终,这不是我们想要的解决方案。但让我们看看当我们将 CSS 修剪到仅包含所需内容时会发生什么,我们可以使用以下脚本执行此操作
const purify = require("purify-css");
const content = ["./dist/**/*.js"]; // Vite-built content
purify(content, ["./css/fontawesome/css/all.css"], {
minify: true,
output: "./css/fontawesome/css/font-awesome-minimal-build.css"
});
当我们加载新构建的 CSS 文件时,我们通过网络传输的 CSS 字节数量大幅下降,从 33KB 降至 7.1KB!

但不幸的是,我们的其他 Font Awesome 字体文件没有改变。

发生了什么?PurifyCSS 履行了它的职责。它确实删除了所有未用图标的 CSS 规则。不幸的是,它无法进入实际的 字体文件 以修剪字形,以及 CSS 规则。
如果有一个像 PurifyCSS 这样的工具可以处理字体文件就好了……
子集化工具来救援!
当然,有一些工具能够从字体文件中删除未使用的内容,它们被称为 子集化工具。一个 子集化工具 分析您的网页,查看您的字体文件,并修剪未使用的字符。有很多用于对字体进行子集化的工具,例如 Zach Leatherman 的 Glyphhanger。事实证明,对 Font Awesome 进行子集化非常简单,因为它自带内置的子集化工具。让我们来看一下。
自动对字体进行子集化
我将要向您展示的自动子集化和手动子集化工具需要付费的 Font Awesome Pro 订阅。
Font Awesome 允许您设置它称为 套件 的内容,在 Font Awesome 文档 中将其描述为“一个背包,可以轻松地将您需要的所有图标和精彩内容装入一个小巧轻便的捆绑包中,并轻松地将其挂在项目后面”。因此,与其导入任何和所有 CSS 文件,套件为您提供了一个可以在 HTML 文件的 <head>
中添加的单个脚本标签,从那里,套件仅从字体文件中发送您实际需要的字体字形。
创建套件大约需要一分钟。您会得到一个类似于以下内容的脚本标签
<script src="https://kit.fontawesome.com/xyzabc.js" crossorigin="anonymous"></script>
当脚本加载时,我们现在根本没有 CSS 文件,并且 JavaScript 文件只有 4KB。让我们再次查看 DevTools 的 Fonts 选项卡,看看在我们进行了一些子集化后加载了哪些字体文件。

我们已经从 757KB 降到了 331KB。这减少了 50% 以上。但是我们仍然可以做得更好,特别是如果我们只渲染 54 个图标。这就是 Font Awesome 的手动字体子集化工具发挥作用的地方。
手动对字体进行子集化
如果 Font Awesome 为我们提供了一个工具,让我们真正挑选我们想要的精确图标,然后为它提供自定义构建,那不是很好吗?好吧,他们做到了。出于某种原因,他们没有大声宣传这一点,但他们实际上确实有一个桌面应用程序,专门用于手动对字体进行子集化。该应用程序可以 从他们的网站下载 - 但与自动子集化工具一样,此应用程序需要付费的 Font Awesome 订阅才能实际使用。

搜索图标,选择字体系列,添加您想要的内容,然后单击蓝色的大构建按钮。这确实是生成 Font Awesome 图标自定义子集所需的一切。
单击按钮后,Font Awesome 会询问它应该将自定义构建保存到哪里,然后它会转储一个包含您需要的所有内容的 ZIP 文件。实际上,您将获得的结构与正常的 Font Awesome 下载完全相同,这使得事情变得特别简单。当然,它允许您将自定义构建保存为项目文件,以便以后可以重新打开它,根据需要添加或删除图标。
我们将打开 DevTools 以查看我们正在加载的图标的最终大小,但首先,让我们看看实际的字体文件本身。自定义构建会创建许多不同类型,具体取决于您的浏览器使用什么。让我们关注 .woff2
文件,这是 Chrome 加载的文件。之前存在的相同 light、regular、duotone、solid 和 brand 文件仍然存在,只是这次没有哪个文件大于 5KB……而且这是在它们被压缩之前!

CSS 文件怎么样?它缩减到只有 8KB。使用 gzip 后,它只有 2KB!
这是 DevTools 中的最终汇总

在结束之前,快速查看一下这些字体文件名。fa-light-300.woff2
字体文件仍然存在,但其他文件看起来有所不同。这是因为 我正在使用 Vite,它决定自动将字体文件内联到 CSS 中,因为它们非常小。

这就是为什么我们的 CSS 文件在 DevTools Network 选项卡中看起来比我们之前在磁盘上看到的 2KB 大一点。权衡是,上面大多数字体“文件”根本不是文件,而是嵌入到此 CSS 文件中的 Base64 编码字符串,从而节省了额外的网络请求。

话虽如此,Vite 正在内联浏览器永远不会使用的许多不同的字体格式。但总的来说,字节数非常少,特别是与我们之前看到的相比。
在离开之前,如果您想知道那个桌面字体子集化 GUI 工具是否有一个可以与 CI/CD 集成以在构建时生成这些文件的 CLI,答案是……还没有。我给 Font Awesome 的人员发了邮件,他们说正在计划一些内容。如果并且当它发布时,这将允许用户简化他们的构建流程。
正如你所见,使用 Font Awesome 之类的图标库非常酷。但默认的使用方式可能并不总是最适合你的项目。为了获得尽可能小的文件大小,我们可以对字体进行子集化处理,剔除不需要的部分,只提供我们需要的内容。尤其是在加载字体方面,我们希望获得这种性能提升,因为字体传统上一直难以处理。
我广泛使用 icomoon 生成我的字体。我添加所需的图标并生成字体。生成的样式将包含 ttf 和 woff。我获取 woff,转换为 woff2,然后在我的 css 中,我使用 woff2 并将 woff 作为后备。
我从来不知道如何解读像这样的性能数据。
你说一个“套件”将字体资源的大小减少了 50% 以上。但是如果我在两个截图中查看“DomContentLoaded”的值,它似乎加载花费了 28% 的时间……
我最近开始在多个项目中使用套件,但看到这一点让我怀疑这是否真的是一个好主意。
我是否真的必须手动进行字体子集化?或者我是否过于重视这个数据?
你没有错,有点。他确实更快地获取了这些文件,但这很可能是因为他是在本地主机上进行的。而套件则必须从服务器获取。所以在这种情况下,它看起来慢得多,但一旦部署到实际环境中,它应该会更快。
显然,他想指出的重点是,在第一种情况下,他必须下载 757KB,而使用套件则只有 331KB。如果在这两种情况下,这些文件都位于同一个位置,我相信你可以想象哪种方式更快以及为什么。
我刚刚将 fontawesome-subset 库集成到我的设计系统中。我可以定义一个包含我想要图标列表的 json 文件,库会生成很小的字体文件。我发现这更好,因为我的团队不需要 Pro 订阅,并且字体生成相对来说是程序化的。
注意事项:它不涉及 CSS,所以需要在 SCSS 中维护第二个映射。它目前也不支持 v6 版本。
字体图标存在许多问题,包括可访问性问题。Fontawesome 建议你使用他们的 JS + SVG 方法而不是字体图标方法。它提供了对图标外观的控制,而 webfont 方法无法提供。
文章中描述的子集化工具适用于 JS + SVG。你也可以直接编辑 JavaScript 文件以删除未使用的定义。
有很多文章说明为什么不应该使用字体图标。这里有一篇:https://www.irigoyen.dev/blog/2021/02/17/stop-using-icon-fonts/
文章中提出的论点都可以解决,特别是屏幕阅读器的问题,这仅仅取决于你如何实现它们。关于字体图标有很多错误信息。它们仍然是一种有效且高效的显示图标的方式。