如果您自己绘制SVG文件或从互联网下载SVG文件,那么像 SVG-Editor 或 SVGOMG 这样的工具就是您的好帮手。使用这些工具压缩文件只需几秒钟,即可大幅减少文件大小。但是,如果您需要内联使用SVG进行动画或与代码交互,那么在代码可读性方面仍然有很多可以做的事情。
使用SVG的<use>
元素重用内容并不总是可行的,但当它可用时,您不会后悔花几分钟时间将其付诸实践。
在本文中,我将展示一个我能够充分利用此元素的示例——不仅是为了减少文件大小,而且还使标记更清晰,更易于阅读和维护。
这是我需要处理的第一个设计。它最初是在Illustrator中创建的。

请查看以下代码,这是直接从软件导出的原始文件,大小为2.05kb:
它根本不是一个很大的文件。但是,打开它,您会发现有很多空标签、已弃用的命名空间、不必要的空格、逗号和软件应用的额外信息。这使得代码难以使用,扫描起来很烦人,并且在文档中数百行代码中造成了很大的滚动。
您还会注意到,该文件确实使用了<use>
和<defs>
元素,但不是以最佳方式使用。这不能怪软件!原始文件中的每个宇航员插图都有一个剪切蒙版:一个无形的圆圈,充当窗口,我们可以通过它看到我们的角色。如果没有它,宇航员的宇航服就会溢出圆圈之外。在Illustrator中有几种方法可以避免这种情况,例如使用路径查找器选项裁剪这些多余的部分。这样,我们可以节省一些字节,并避免仅为我们不会显示的图形的剪切信息使用额外的圆圈。文件的压缩始于软件。 尽管如此,如果我们不想编辑原始文件,我们仍然可以在代码上进行很多改进。
使用SVGOMG压缩SVG并保留默认选项不需要任何努力,您将获得一个大小为1.46kb的文件。与原始大小相比,这减少了30%,并且图形看起来完全相同。
重用内容
这将需要遍历SVG并进行一些调整。我知道与前面的示例相比,此选项需要更多时间,但它并不像看起来那么难。
我们有一个重复的元素,即圆圈内的宇航员。这就是我们在SVGOMG上压缩的那个。结果将如下所示
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 95.8 95.8">
<style>.st3,.st4{fill:#ffcb2f;stroke:#12192c;stroke-width:1.4891;stroke-miterlimit:10}.st4{fill:#69b2b1}</style>
<circle cx="47.9" cy="47.9" r="47.9" fill="#12192c"/>
<circle cx="47.9" cy="47.9" r="40.7" fill="#f6a2a4"/>
<defs><circle id="SVGID_1_" cx="47.9" cy="47.9" r="40.7"/></defs>
<clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath>
<g clip-path="url(#SVGID_2_)">
<path class="st3" d="M63.9 45.6H32c-4 0-7.2 1.9-7.3 4.3l-.8 26.6H72l-.8-26.6c-.2-2.5-3.4-4.3-7.3-4.3z"/>
<path class="st4" d="M74.3 86.9L66 88.2C53.8 90 41.4 90 29.1 88.1l-7.7-1.2v-14c0-4 3.2-7.2 7.2-7.2h38.5c4 0 7.2 3.2 7.2 7.2v14z"/>
<path class="st3" d="M31.8 47.3h-.6c-.7 0-1.2-.6-1.2-1.2V23.2c0-.7.6-1.2 1.2-1.2h.6c.7 0 1.2.6 1.2 1.2v22.9c0 .7-.6 1.2-1.2 1.2z"/>
<circle class="st4" cx="31.5" cy="20.7" r="2.8"/>
<circle class="st4" cx="47.9" cy="51.4" r="20.3"/>
<path d="M64.5 53.1c0 8-7.4 11.2-16.5 11.2S31.4 61 31.4 53.1s7.4-14.4 16.5-14.4 16.6 6.4 16.6 14.4z" fill="#13192d" stroke="#12192c" stroke-width="1.489" stroke-miterlimit="10"/>
<path fill="none" stroke="#12192c" stroke-width="1.489" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-dasharray="9.6793,3.7228" d="M65.9 88V76.9"/>
<path fill="none" stroke="#12192c" stroke-width="1.489" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M29.6 87.9v-11"/>
</g>
</svg>
首批建议:
- 将
<style>
内容移动到CSS文件中(假设您可以内联使用SVG,并且您的文档中链接了样式表)。 - 使用对您有意义的内容重命名ID。
- 舍入那些复杂的数字,例如将
stroke-width="1.489"
舍入为stroke-width="1.5"
。如果您在Illustrator中调整矢量大小并选中缩放边框选项,则可能会发生这种情况。 - 删除
stroke-miterlimit="10"
,因为我们不需要它,因为我们的stroke-linejoin
是圆形的。 - 此代码将是我们的宇航员模板。我们需要将所有内容包装在一个组中,向该组添加一个ID,并将其放置在
<defs>
标签内。请注意,我们已经有一个包含圆圈的<defs>
元素。我们可以删除它,因为它将成为更大的<defs>
标签的一部分。
请注意,前两个圆圈是具有不同半径和颜色的填充形状。我们可以保留较小的那个,并添加一个足够大的描边以达到相同的效果——同样,这是我们在Illustrator中最初可以使用带边框的圆圈避免的事情。
另一件重要的事情是,我们当前的viewBox对于我们想要构建的内容来说太小了。让我们将其放大,并在X轴上添加一些负空间,以便我们可以从中间开始克隆我们的宇航员。
要了解有关viewBox的更多信息,请查看Amelia Wattenberger编写的这篇优秀的指南,了解如何缩放SVG。
最终结果将如下所示
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-400 0 1000 5000">
<defs>
<g id="astronaut">
<circle cx="94.5" cy="48" r="44" fill="currentColor" stroke="#12192c" stroke-width="8"/><clipPath id="a"><circle cx="94.5" cy="47.9" r="40"/></clipPath>
<g clip-path="url(#a)"><path class="st3" d="M110.5 45.6H78.6c-4 0-7.2 1.9-7.3 4.3l-.8 26.6h48.1l-.8-26.6c-.1-2.5-3.4-4.3-7.3-4.3z"/><path class="st4" d="M121 86.9l-8.3 1.3C100.4 90 88 90 75.8 88.1l-7.7-1.2v-14c0-4 3.2-7.2 7.2-7.2h38.5c4 0 7.2 3.2 7.2 7.2v14z"/><path class="st3" d="M78.4 47.3h-.6c-.7 0-1.2-.6-1.2-1.2V23.2c0-.7.6-1.2 1.2-1.2h.6c.7 0 1.2.6 1.2 1.2v22.9c0 .7-.5 1.2-1.2 1.2z"/><circle class="st4" cx="78.1" cy="20.7" r="2.8"/><circle class="st4" cx="94.5" cy="51.4" r="20.3"/><path d="M111.1 53.1c0 8-7.4 11.2-16.5 11.2S78 61 78 53.1s7.4-14.4 16.5-14.4 16.6 6.4 16.6 14.4z" fill="#13192d" /><path fill="none" stroke="#12192c" stroke-width="1.5" stroke-linecap="round" d="M112.5 88V76.9"/><path fill="none" stroke="#12192c" stroke-width="1.5" stroke-linecap="round" d="M76.3 87.9v-11"/></g>
</g>
</defs>
</svg>
<defs>
内部的内容不会在任何地方渲染。要开始克隆我们的宇航员,我们需要像这样在其内部链接其ID到<use>
元素中
<use xlink:href="#astronaut"/>
xlink:href
自SVG2以来已被弃用,但最好出于兼容性目的使用它。您可以在现代浏览器中使用href,但我已在Safari上对其进行了测试,截至撰写本文时,它无法正常工作。如果您使用xlink:href,请确保在您的SVG标签中包含此命名空间:xmlns:xlink="http://www.w3.org/1999/xlink
(如果您决定使用href
,则不需要它)。
现在,我们可以向此第一个图形添加相应的文本,并使用transform属性对其进行对齐。我们最好将这两个元素都放在一个组内,以便将来我们可以将整个组转换为我们想要的位置
<g transform="translate(-95 210)">
<use xlink:href="#astronaut"/>
<text transform="translate(25 130)">Tech Leader</text>
</g>
连接线是简单的形状,可以使用<path>
直接绘制。路径看起来很吓人,但对于矩形线,无需担心太多。我将解释此代码
<path class="line" d="M-4 200v-25h200"/>
d=""
用于数据,我们将在其中放置我们的命令。M
用于将我们的手移动到我们将开始绘制的地方(但它没有绘制任何东西)。-4 200
表示我们将铅笔向左移动四个单位,向下移动200个单位到我们的viewBox(遵循SVG坐标系的方位)。v
是开始绘制垂直线的命令,该命令将从此处向上移动-25个单位。h
用于水平线,因此我们从那里向右绘制一条200个单位的线。感觉就像Logo设计软件。
我将线条分成了三条路径,但我们只需使用一条,在命令序列后使用M
值,即可将我们的手移动到坐标系中的新点并开始从该点绘制。
请查看最终文档。现在文件大小为779字节,并且有12行易读且可扩展的代码
如果我们在<defs>
中定义的属性中声明任何值,那么由于<use>
元素的性质,将无法在其克隆中更改它。这就是为什么在上面的示例中,主圆圈的填充被替换为currentColor
的值,以便控制所有复制项的背景。currentColor
将继承元素(或其上方的任何元素)的CSS颜色值。在SVG中,我向一些复制的宇航员添加了一个类,并在CSS中向这些类添加了一个颜色值。这样,我就可以更改具有该类的<use>
元素的所有实例。要了解有关<use>
以及如何对其内容进行样式设置的更多信息,这篇帖子由Sara Soueidan撰写,其中包含您需要了解的所有内容。
使用此准备好的代码,我们可以更轻松地将图形扩展到类似这样的内容

以下是三个并排的示例,用于比较可读性和代码量,我们从241行减少到10行整洁的代码

很高兴指出
<use>
的使用,我每天都在使用它,但我使用的是<symbol>而不是SVG中的<defs>。这难道不是symbol元素存在的最初原因吗?iGadget说得对!你说得对,我本可以使用
<symbol>
而不是<defs>
。我想在我的脑海中,<symbol>
更适合系统(例如图标系统)。在这个示例中,我只使用了一个重复的图形,并没有利用<symbol>
提供的属性,例如viewBox、title、preserveAspectRatio等。很棒的文章!很高兴看到您对流程和绘图顺序的分解。
谢谢alioso!我很感激!
比较gzip压缩前后文件大小会很有趣。由于压缩在存在重复数据时效果最佳,因此多次定义相同的元素对压缩后的文件大小的影响应该可以忽略不计。
当然,提取元素对于维护来说更好,因为遵循了DRY原则,但根据我的经验,SVG很少手动编写/编辑。大多数情况下,设计人员会导出文件并经过一些自动优化过程,然后才能在网站上显示,无需任何手动干预。
完全正确!Gzip始终是我们的好帮手。
我明白手动优化不是常见的情况。我经常使用它,因为我经常进入代码以添加动画类。
在这个具体的示例中,我们使用该组织结构图仅显示团队中现有的角色,因此它会根据每个团队的结构动态显示或隐藏信息。能够在代码中拥有这种简洁性对于此任务确实很有用。