当我们生成 PNG 图片时,我们使用 <img>
标签或 CSS 背景,仅此而已。它非常简单且保证有效。
不幸的是,尽管 SVG 拥有诸多优势,但它却并非如此。虽然在 HTML 中使用 SVG 时您可以有多种选择,但实际上归结为 内联
、<object>
和 <img>
,所有这些都存在严重的陷阱和权衡。
内联 SVG 的问题
如果您使用内联 SVG,则会失去使用浏览器缓存、服务器和浏览器之间的 Gzip 压缩以及搜索引擎图像索引的功能(内联 SVG 不被视为图像)。即使您的图像可能一点也没有改变,它们也会始终重新加载,这会导致网站加载速度变慢,大多数人都不愿意容忍这种权衡。
此外,内联 SVG 还会导致复杂的依赖关系问题,您无法轻松地将图像插入 HTML,而必须求助于脚本(PHP 或其他)。当您拥有多个图像时,这在维护站点方面会成为一个巨大的问题,因为从本质上讲,您无法再使用 <img>
标签了。
毫无疑问,内联 SVG 在某些方面确实很出色——也就是说,如果您希望您的图像快速显示,而无需等待其他资源加载。除此之外,显然,您不能只内联所有内容。
object 标签的问题
SVG 以其在所有分辨率的设备上显示的出色质量以及引用外部资源(如 CSS 和字体)同时保持文件大小非常小的能力而闻名。其理念是拥有许多都共享单个 CSS 或单个字体文件的 SVG,以减少您需要下载的资源数量。
资源共享的神话
许多人不知道,共享 SVG 的外部资源仅适用于内联 SVG。由于 <object>
标签的使用允许访问这些资源,因此人们认为浏览器将下载 CSS 的单个副本,即使您有许多 <object>
标签都引用了同一个 CSS 文件。
然而,这根本不是真的

使问题更加复杂的是,<object>
标签不被识别为图像,因此图像搜索索引是不可能的。
进一步加剧问题的是依赖关系问题。例如,假设您有 100 张图片,其中 25 张使用 Roboto 字体,另 25 张使用 Lato,25 张使用 Open Sans,其余则使用这三种字体的组合。您的 CSS 需要引用所有三种字体,因为要跟踪哪个文件使用哪种字体是相当不可能的,这意味着您可能在某些页面上加载了不需要的字体。
图片标签
这让我们只剩下 <img>
标签,它有很多优点。因为它与其他图像格式使用的标签相同,所以您可以获得熟悉感、浏览器缓存、Gzip 压缩和图像搜索。每个图像都是独立的,没有依赖关系问题。
<img>
标签一起使用,SVG 会丢失字体唯一的问题是 **您将丢失字体**。更准确地说,如果您的 SVG 中有任何文本,除非您嵌入字体,否则您的文本将使用系统字体显示,通常是 Times New Roman。您花了几个小时选择了一种漂亮的字体,但当您使用 <img>
标签嵌入 SVG 时,所有这些都消失了。这怎么可能被接受呢?
调查字体光栅化
我们的第一反应是看看是否可以执行字体光栅化。这是一种常用的将字体转换为路径的技术,因此它可以在所有设备上良好渲染并保持零依赖性。从表面上看,这工作得非常好,并且在编辑器中,一切看起来都很完美。
尽管光栅化后的 SVG 的大小高达 137 KB,而光栅化前的 SVG 大小为 15.7 KB,但我们仍然很乐观,因为在使用 Gzip 压缩优化 SVG 后,光栅化后的文件大小缩减至 11 KB,略小于等效 PNG 的 11.9 KB。
原始 | 字体光栅化 | 字体光栅化(.svgz) |
---|---|---|
15.7 KB | 137 KB | 11.0 KB |
PNG @ 1x | PNG @ 2x | PNG @ 3x |
---|---|---|
11.9 KB | 26.5 KB | 42.6 KB |
唉,一旦我们将光栅化后的 SVG 嵌入到 HTML 中,我们就发现我们的乐观情绪是过早的。虽然它在高分辨率显示器上看起来可能很棒,但在低分辨率显示器上的质量却无法接受。

图像底部是原始字体,字体清晰显示,而顶部是使用字体光栅化的字体,字体呈像素化。

发生的情况是,大多数操作系统都会优化字体,以确保它们在所有屏幕上都清晰锐利地显示。在 Windows 上,这称为 ClearType,并且由于我们对字体进行了光栅化,因此不会应用任何优化,从而导致文本模糊,尤其是在低分辨率屏幕上。
显然,质量下降是不可接受的,所以让我们重新开始。
字体嵌入来救援
最初,我们对字体嵌入持极大的怀疑态度,主要是因为工作流程复杂。
要在 SVG 中嵌入字体,您首先必须知道使用了哪些字体系列。然后,您需要找到这些字体文件并下载它们。下载完成后,您需要将常规、粗体、斜体和粗斜体转换为 Base64 编码。如果您手动执行此操作,在大量文件中,要确定哪个文件使用粗体而哪个文件不使用粗体是不可能的。然后,您必须将所有四个 Base64 编码字符串复制到您的 SVG 中。
当然,一定有更好的方法。这就是我们构建 Nano 的原因。Nano 会自动扫描 SVG 并仅插入使用的字体。例如,如果未使用粗体或不存在文本,则不会嵌入任何字体。
尽管如此,生成的文件仍然很大,并且与等效的 PNG 相比没有竞争力,因此我们继续努力并构建了自己的 SVG 优化器 (Nano),它会将 SVG 文件大小缩减到极小。(了解 Nano 如何压缩 SVG。)此外,我们还优化了将字体嵌入 SVG 的方式,从而使文件大小变得非常小。
比较文件大小和带宽节省
原始 | 字体光栅化 | 未优化的字体嵌入 | Nano 字体嵌入 | |
---|---|---|---|---|
大小 | 15.7 KB | 137 KB | 65.2 KB | 22.0 KB |
Gzip | 3.57 KB | 11.0 KB | 44.5 KB | 11.7 KB |
PNG @ 1x | PNG @ 2x | PNG @ 3x | |
---|---|---|---|
大小 | 11.9 KB | 26.5 KB | 42.6 KB |
Gzip | 12.1 KB | 26.1 KB | 41.7 KB |
从上面可以看出,即使嵌入字体,Nano 生成的 SVG 也是非常轻量级的,大小为 11.7 KB(Gzipped),而等效的 PNG @1x 大小为 11.9 KB。虽然这看起来微不足道,但您网站上节省的总带宽肯定非常可观。
假设您 50% 的流量是低分辨率,40% 是 2X 分辨率,其余 10% 是 3X 分辨率。如果您的网站在一张图片上获得了 10,000 次点击
(5,000 * 11.9 KB) + (4,000 * 26.5 KB) + (1,000 * 42.6 KB) = 208.1 MB
如果你使用 Nano,并使用 GZip 压缩 SVG
10,000 * 11.7 KB = 117.0 MB
这将导致:208.1 MB – 117.0 MB = 91.1 MB
的节省,或 43.7% 的带宽节省,无论以任何标准衡量,这都是一个相当可观的数字。
除了带宽节省之外,你还可以获得更简单的流程,无需使用多个带有多个 srcset
的 PNG 图片,并且拥有更好的质量,包括操作系统字体增强功能,以确保你的图片在所有分辨率的设备上保持清晰锐利。最棒的是,为你的用户提供更好的用户体验,因为你的网站加载速度会更快——尤其是在高分辨率设备上。
彻底测试 Nano
我们对这些节省并不满足,于是开始寻找 SVG 图片来彻底测试 Nano。我们使用了总共 2,571 个不同大小和设计的 SVG 文件,总计 16.3 MB
。经过 Nano 优化后,大小变为 6.2 MB
,惊人的 61.8% 的尺寸节省。

显示视觉差异
由于我们正在测试的文件数量非常庞大,并且随着时间的推移还在增加,因此我们必须构建一个自动测试,包括自动突出显示优化前后的像素差异。其他 SVG 优化器的抱怨之一是,SVG 的最小化可能会破坏你的图片,导致其渲染效果与原始图片不同。
为此,我们将像素差异化引入到 Nano 的自动测试中。也就是说,如果 Nano 检测到它优化的 SVG 与原始 SVG 相比像素差异超过 1%,它会发出警告,从而确保 Nano 的优化永远不会破坏 SVG。
由于字体是嵌入式并保留的,加上 SVG 是一种矢量图形格式,因此所有分辨率下的渲染质量都无法与其他光栅格式相比。
接下来是什么?
我们希望我们的工作能够让 SVG 更容易在任何地方使用。我们目前正在努力生成更小的文件大小,并将我们的代码移植到 Node.js 上,以便你可以使用 Nano 自动化你的生产构建,等等。
你认为 Nano 会对你的项目有所帮助吗?请在评论中告诉我!
如果 W3C 没有被企业 CSS 狂热分子控制,那么 SVG 字体就不会被弃用。SVG 字体可以解决你所有的问题,但你却无法使用它们。感谢 Google、Apple 和 NSA。
感谢这篇文章。看起来,如果你只有几个地方在 SVG 中显示字体,那么简单地将文本转换为路径也是一种选择。这假设了一些事情,比如“轻量级”(点数不多)的字体,以及只有几个字符。然后路径将保留为 SVG 矢量,而不是栅格化。图片标题写着“将字体转换为路径”,但随后又讨论了栅格化,而没有更多地提及使用路径转换的字体。我有点困惑。
嗨,Kel,感谢你的反馈。我们将字体转换为路径称为栅格化,本质上你只是将字体栅格化成路径。即使对于“轻量级”文本,我们也**不建议**将其转换为路径,因为输出质量会下降,因为这些路径可能无法清晰显示。另一方面,我们推荐 Nano,它会自动确保字体嵌入尽可能轻量级,从而产生清晰的字体,这些字体会由操作系统优化,并且 SVG 文件大小非常小。
谢谢 Thomas。之所以感到困惑,仅仅是因为我遇到的其他大多数应用程序都使用“栅格化”一词来表示转换为光栅图像,而转换为路径仍然是表示路径的更轻量级的代码 :)
谢谢。我喜欢网络上的 SVG,这对我是个好消息。
有没有可能将其做成 Sketch 插件?
@smlombardi,目前还没有计划,但它已经在 vecta.io 中成为一个插件。请在 Google 上搜索,因为使用 vecta 完成的绘图在导出 SVG 时将有一个选项可以压缩或最小化并嵌入字体。希望这有帮助。
忘记提到正在开发一个 Node.js 模块来帮助你自动“nanofy”你的 SVG,这使得将其包含在你的构建过程中变得更加容易。
我不同意你在本文中列出的 SVG 的缺点。你可以在 nginx 中轻松为 SVG 启用 gzip,浏览器缓存也不是问题,即使是问题,你的服务工作者也可以为你缓存它。
嘿,AJ,你当然可以在服务器上启用 gzip,但如果你正在内联 SVG,它们将**不会**被识别为 SVG,因此没有传输编码和缓存清除。希望这能澄清一些事情。
这篇文章可能会受益于一个更合适的示例。这些图片显示了包含图标和文本的简单控件的示例。这通常由 SVG 图标和常规(HTML)文本处理,然后可以通过 CSS 轻松地将其设置为自定义字体。为什么还需要将文本也嵌入到 SVG 中?我假设,一个更好的例子可能是 SVG 信息图表或图表。
@Sime,你说的都对。在我们的示例中,我们想展示仪表盘的一部分,最简单的方法是将它们与文本一起绘制,并将整个内容视为图像。信息图表或图表肯定是一个更好的例子。将图像分离成图标和 HTML 文本可能难以实现。即使是最优秀的团队也会犯这些错误,请参见 https://cloud.google.com/storage/,单个 API 图像以及他们与 AWS 的定价比较。他们可能安装了 Roboto,并且在他们的系统上看起来很棒,但对于我们其他人来说,我们可能更愿意不默认使用 Times New Roman。
为什么不将外部 SVG 动态内联到 DOM(缓存),然后销毁/重新创建你当前所有 SVG USE 引用?就是这样。缓存的内联 SVG 始终支持字体,一直到 IE。
@Chris LaChance,这是一个非常棒的想法,尽管有一些缺点。
通过嵌入字体,你可以像对待任何其他图片一样对待图片,并让浏览器完成繁重的工作。这仍然是一个很棒的想法,并且有其用途。干杯!
是的,在这种情况下,确实需要 DOM 就绪,至少对于不支持外部引用的浏览器来说是如此。我确实喜欢可以嵌入文本。这使得管理设计系统变得更加容易。
使用 Nano 优化后最终的 img 显示没有视觉差异,在 Chrome、Safari 和 Opera 中看起来很棒。但在 Firefox Quantum 60.0.2 中,轮廓文本丢失了提示,根本无法阅读。它一定是在 WebRender 的工作方式上遇到了问题。
嘿,Sandy,非常感谢你的反馈。
关于您提到的最终图像,这是一个 Quantum 浏览器渲染的错误,它无法正确渲染包含 `USE` 元素的字体,导致字体没有描边,但有趣的是,原始图像却能正常渲染。我们会向他们提交一个错误报告。目前,我们会替换图像并删除 `USE` 元素,再次感谢您告知我们!
此外,我们的测试表明 Quantum 存在一个错误,它拒绝接受没有单位的 SVG 属性值,特别是 `font-size` 属性。根据 SVG 规范,如果没有单位,则默认应用 “px”,因此,如果您的 SVG 中有 `font-size="45"`,则会被理解为 `font-size="45px"`。
Nano 作为 SVG 压缩工具,显然会去除额外的 “px”,导致 Quantum 将 `font-size="45"` 标记为无效属性,从而导致渲染问题。我们会向 Quantum 团队提交一个错误报告。
如果您遇到任何其他问题,请告知我们,我们希望 Nano 能够在所有平台上都能正常工作。谢谢!
大家好,文章和工具很棒 - 非常感谢。但是,也许我只是犯了愚蠢的错误 - 我尝试使用 Nano 将字体嵌入到我的 SVG 中,但输出文件不包含任何嵌入的字体(因此出现了很多 Times New Roman :-(
我尝试上传一个已经包含以 base64 编码的 CSS 格式的字体的 SVG 文件(被 Nano 去除了),使用链接的网络字体(完全没有效果)以及 Sketch 或 Illustrator 的纯输出文件(在 Nano 处理后的最终结果中也没有嵌入字体)。
我一定遗漏了一些非常明显的东西,但您能帮我一下吗?原始 SVG 文件必须如何包含字体才能使 Nano 发挥其魔力?
任何帮助都将不胜感激!