我一直主张使用 SVG 图标系统,现在依然如此。简单说来,它有很多优点:基于矢量的图标在高像素密度世界中看起来很棒,SVG 提供了大量的设计控制,并且它们的可预测性和性能也很高。
我还经常提倡使用基于 <symbol>
(“SVG 雪碧图”)和 <use>
元素放置它们的 SVG 图标系统。我改变了一些想法。我认为这并不是一个糟糕的方法,但肯定存在一个更简单(也许也更好)的方法。
直接将图标嵌入代码中。
就是这样。如果您希望看到更复杂的方法,请原谅。
就像这样
<button>
<svg class="icon icon-cart" viewBox="0 0 100 100" aria-hidden="true">
<!-- all your hot svg action, like: -->
<path d=" ... " />
</svg>
Add to Cart
</button>
或者更实际一点,使用您选择的服务器端包含
<button>
<?php include("/icons/icon-cart.svg"); ?>
Add to Cart
</button>
就像我说的
<?php include "icon.svg
<% render "icon.svg"
<Icon icon="icon"
{% include "icon.svg"直接将图标放入标记中,这是一个非常 👍 的图标系统。
— Chris Coyier (@chriscoyier) 2017 年 5 月 31 日
优点 #1:无需构建过程
您无需任何花哨的工具来实现这一点。您的包含大量 SVG 图标的文件夹仍然是一个包含大量 SVG 图标的文件夹。您可能需要优化它们,但仅此而已。
优点 #2:没有 Shadow DOM 问题
作为 <use>
引用包含的 SVG 图标具有 Shadow DOM 边界。

这很容易造成混淆。例如
var playButton = document.querySelector("#play-button-shape");
playButton.addEventListener("click", function() {
alert("test");
});
这将无法正常工作。您将定位 <symbol>
中的路径,这实际上不会做任何事情,而且点击处理程序会在克隆过程中丢失。您必须将类似的处理程序附加到父 <svg>
,例如 #play-button
。
同样,类似这样的 CSS 选择器
.button #play-button-shape {
}
将不会选择任何内容,因为这两者之间存在 Shadow DOM 边界。
当您直接将内联 SVG 放入适当位置时,将不会存在 Shadow DOM 边界。
优点 #3:仅使用您需要的图标
在 <use>
/<symbol>
系统中,您有这个 SVG 雪碧图,它很可能包含在每个页面上,无论它们是否在任何给定页面上都被使用。当您直接包含内联 SVG 时,页面上的唯一图标是您实际使用的图标。
我将此列为一个优点,但这有点两面性。公平地说,可以缓存 SVG 雪碧图(例如,使用 Ajax 获取并注入页面),这将非常高效。
@Real_CSS_Tricks SVG <use> 的缓存友好性如何? #SVG #CSS
— Samia Ruponti (@Snowbell1992) 2017 年 6 月 7 日
这是一个有点棘手的问题。<use>
本身与缓存无关,它与 <use>
所引用的 SVG 的位置有关。如果雪碧图是使用 Ajax 获取的,则可以缓存它。如果雪碧图只是 HTML 的一部分,则可以缓存该 HTML。或者 <use>
可以指向一个外部文件,并且可以缓存该文件。这很有吸引力,但是…
优点 #4:没有跨浏览器支持问题
IE 或 Edge 浏览器无法做到这一点
<use xlink:href="/icons/sprite.svg#icon-cart" />
也就是说,通过相对文件路径链接到图标。它在 Microsoft 环境中唯一可行的方法是引用同一页面上的 SVG 的 ID。为此有一些变通方法,例如使用 Ajax 获取雪碧图并将其转储到页面上,或者使用像SVG for Everybody 这样的库,该库检测浏览器支持情况,并根据需要使用 Ajax 获取所需的部分 SVG 并注入它。
潜在的小问题:HTML 缓存膨胀
如果您最终使用了雪碧图路线,就像我说的,您可能会很想使用相对路径链接到雪碧图,以利用缓存优势。但 Microsoft 浏览器会破坏这一点,因此您需要在以下两种选择之间做出选择:
- JavaScript 解决方案,例如使用 Ajax 获取整个雪碧图并将其注入,或使用 polyfill。
- 在服务器端将雪碧图转储到 HTML 中。
我发现自己更经常做 #2,因为 #1 会导致异步加载图标,这感觉很卡。但使用 #2 意味着“膨胀的”HTML 缓存,这意味着这个雪碧图在每个唯一的 HTML 页面上被缓存了一遍又一遍,效率不高。
直接内联 SVG 也会出现同样的情况。

结论和 TLDR:由于其简单性、优势和微不足道的缺点,我预计直接内联 SVG 图标将成为处理 SVG 图标系统最流行的方式。
我们发现,这种方法的一个(对我来说有点意外的)缺点是,如果您使用 React 渲染页面:如果您的页面上有大量图标,React 渲染所有图标内各个元素所需的工作量会导致明显的加载时间增加,而使用
<use>
标签则不会出现这种情况。如果您使用 React(以及/或其他框架),直接内联基本上等同于将一大块
<symbol>
代码块放到您的 html 中?您最终会得到一个位于 js 包中的雪碧图。我是否应该将图标拆分成单独的包?这个问题存在的时间够长了吗,是否有正确的方法™来处理它?这是否会造成很大的开销,成为一个问题?
我不确定 React/JS 框架与它有什么关系,但基本上,文章中概述的区别与框架无关。
处理 SVG 的最佳方法始终围绕着您的使用场景。没有“正确的方法”,因为各种方法都有其优缺点。例如,在我现在正在开发的网站上,我需要能够动画化 SVG 路径。如果我使用外部引用的 'myfile.svg#foo' 方法,我可以利用跨页面的缓存,但这会限制我动画化其中路径的能力(Shadow DOM 问题)。
为了正确回答您的问题,您需要评估所有不同的 SVG 方法以及您的需求。
我在首次加载时内联一个雪碧图,但也会将它放在 localStorage 中,并使用内联脚本从那里内联它,以便在后续页面中使用。没有明显的卡顿。
您能详细介绍一下这种技术吗?
+1 Russell Bishop 的问题。
我已经用了一段时间了,使用 pug/jade 混合方法在编译时将 svg 包含到我的 HTML 文件中。我必须说,它不如我希望的那么易于维护,但它功能强大且灵活。
您需要在混合方法中维护一个“case”语句,为每个图标添加一行,并将您的 SVG 转换为 pug 文件。
这是一个简单的示例
https://codepen.io/albpara/project/editor/DBaYKn/#
酷酷酷
如果你正在使用 SVG 图标制作按钮,无论你如何显示它,图像都应该在
<button>
中。你不能在按钮中使用 `:after` 作为图标,并使用 svg 作为背景吗?尽管 svg 没有功能 :(
如果你不想使用任何 svg 功能,你可以使用经典的方法,在按钮上使用 `:after` 或 `:before`,并将 svg 设置为该元素的背景。如果我们仍然能够以这种方式样式化和编辑 svg,那会很棒,但遗憾的是… :(
你可以这样做,但我认为 SVG——无论你是通过内联还是背景图像显示它——都应该是一个独立的元素,位于按钮元素的 **内部**。
嗯,为什么呢?
我认为这是样式和功能分离。
对于 Angular2 的 basehref 问题,使用 svg 中的 # href 有什么好的解决方案吗?
一个当前标准的例子是使用一个简短的 html 片段(比如
<i>
)和一个类名(比如<i></i>
)。如果只是浏览器支持问题,我可以等 6 个月或一年,它对任何特定系统不再是一个问题?(我用这种方法等过很多很多黑客和浏览器漏洞,效果很好)
如果你的代码库中有成千上万行的内联 SVG 代码散落在你的模板、JS 文件等中,那么如何在 IDE 中使用它?(更不用说文件大小问题了)(也许你是指简单的网站?)
与其他方法相比,内联方式更容易维护图标的更改吗?(例如,
<i></i>
)内联方式比直接使用 有什么优势?
你似乎想用这种方法实现一些特定的目标,但没有说明。你是否打算用 CSS 样式化图标的特定元素,而这只有内联方式才能实现?也许你的目标集涵盖了一些其他特定的需求,而普通图像或背景图像不能很好地满足这些需求?(不考虑浏览器支持问题)
我将所有图标保存在一个 XML 文件中,该文件通过 simpleXML 在每个页面上都变成 php 变量,我用内联方式将这些变量输出。这样,图标可以用文本、注释或空内容替换。就像苏联一样,只是用图标代替了人——而且用的是 XML!
我还没准备好回答这个问题,但听起来还不错!
完全同意 Chris。我多年来一直在使用 symbol / use 图标系统,最近才意识到,为了页面上的 4 个社交图标,整个 gulp 任务、图标分离、IE 的 javascript 依赖以及编译,都是过度杀鸡。我切换到了你的新方法——内联所有内容。感谢你的文章。
这不是问题。我多年来一直在广泛使用 SVG 图标。
只需使用
<img>
标签,在 SVG 文件/标签中指定 viewBox 属性以缓解浏览器兼容性问题,并确保 HTTP 响应头中提供正确的媒体类型!这里有两个缺点……
但是,如果这些问题不重要,那么是的,这甚至更容易。
我已经尝试过几种不同的 SVG 图标方法,但当我确定不需要任何脚本时,我总是回到使用
<img>
标签。每个图标最初可能需要一个单独的 HTTP 请求,但如果图标在全局使用并且浏览器缓存了 SVG 图像文件,那么这可能是一件好事。
如果你的服务器配置了 http2,多个请求不会成为问题吗?
你无法更改作为 标签的 svg 的外观。
Edge 13 及更高版本支持使用外部资源的 SVG (https://developer.microsoft.com/en-us/microsoft-edge/platform/changelog/desktop/10547/).
还有一个 polyfill 不使用浏览器嗅探:https://github.com/Keyamoon/svgxuse。
我最近一直在使用这个方法,它在 IE 上运行良好。图标出现之前会有短暂的延迟,但我认为这是可以接受的。
我也很喜欢 svgxuse。
我在我当前的项目中一直在使用这种方法。
可维护性不是问题,假设你使用的是某种模板——最好是服务器端的。(我可怜所有为客户端模板的糟糕性能而挣扎的人。虽然我理解从一些 API 获取数据并将其在浏览器中转换为标记的优势……但在大约 90% 的项目中,这都是一个性能杀手。)
因此,我在使用 Rhodes 框架的混合移动应用程序中这样做。应用程序中有一个 Rails(类似)服务器和 ERB(我知道……我也觉得 ERB 很痛苦,但这是可用的)模板。图标是从 Illustrator 中保存的,并带有实体样式,因此你可以轻松地使用 CSS 覆盖它。图层以语义方式命名,因此你可以获得语义 ID。一个简单的预构建工作流程将 SVG 中的
id=
更改为class=
。因此,现在,我可以使用语义 CSS 类名来更改颜色、动画等。.svg
文件被重命名为_whatever.svg.erb
并放置在一个模板引擎可访问的目录中。因此,现在每个图标都是一个 ERB 部分。是的,使用手动编辑(或工作流程),我可以将部分或其他 ERB 标记包含在图标中。我目前在一个图标上这样做,它是一个静态的圆形进度条。(静态,因为进度在查看带有图标的页面时永远不会改变。)
progress_percent
作为局部变量传递到部分中。我计划对 SVG 中的文本进行 i18n。每个短语都放置在一个图层上,并为图层赋予一个有意义的名称,该名称可用于从表格中查找翻译。相同的预构建工作流程可用于将特别标记的
<text>
元素的内容替换为例如<%= t :t_submit_btn_lbl %>
(其中t
是我的翻译助手)。在这个特定环境中,模板渲染时间不是问题,因为模板在构建时被编译成 Ruby,然后 Ruby 被编译成 Ruby 字节码。在运行时,它只是执行 Ruby 字节码,并且一旦文件被加载,它就会保持加载状态。
另一个好处是
<use>
比直接绘制形状需要浏览器消耗更多的 CPU是的,我这样做已经有一段时间了。非常喜欢这个解决方案。
感谢你的认可!
我仍然更喜欢内联在 CSS 中的 SVG 精灵,可以使用
<i class="[size] [color] [type] icon">[Fallback]</i>
来显式使用。与将 SVG 放入 HTML 相比,唯一的缺点是每个颜色都需要一个额外的语句。能够在不增加页面大小的情况下重复使用图标绝对是一个优势。阻止我们使用内联图标的是我们使用的 CMS 不支持它们。我们还需要让我们的用户/客户在添加 CTA 框等时输入图标。我们使用一个 SVG 精灵和一个简单的类名来添加所需的图标,使用
::before
和::after
元素——使用 https://www.liquidlight.co.uk/blog/article/creating-svg-sprites-using-gulp-and-sass/ 中的技术构建不确定这是否可以被认为是性能方面最好的方法,但在尝试过许多不同的方法后,我最终找到了我认为在使用 React 时的首选实现:每个图标简单地定义为其自身组件中的内联。这使得使用和更新图标变得非常简单,并且不会牺牲任何 SVG 功能。此外,它使用 React 扩展功能变得非常简单(例如,我有一个文件占位符图标,它有一个文本元素,该元素根据文件类型而改变,我只需通过道具传递它)。
我将所有 SVG 都作为内联使用,它们来自 php 函数调用。
但是裁剪怎么办?你需要为 clipPath 使用 ID。如果你多次放置 svg 图标,你必须更改所有裁剪 ID。你知道更好的方法吗?
这基本上是我实现 vue-awesome 的方式 :D
当您在一个页面上大量使用一个比较复杂的 svg 符号时,会不会有问题?例如,我有一个文章列表,每个文章标题前都显示一个小图标来“分类”文章。如果我将每个文章的 svg 图片内联,我的 html 文件会变得很大。使用 USE 系统是一个有效的问题吗?
这也是我主要的问题。在我的情况下,如果将所有图标内联,我的文件大小会呈指数级增长,这根本不是一个一劳永逸的方案。
内联似乎解决的问题是允许控制 SVG。但如果您不在乎这样做,那么将 SVG 作为背景,或者作为 ::after/::before 插入等等(其他系统)都能正常工作,甚至比内联 SVG 更好。
断言内联 SVG 是最佳方式,就是忽略了许多情况下它实际上是最糟糕的方式。
的确,在上个月,我开始更好地理解为我的个人博客管理图标和图片的最佳方法。特别是,我了解到,使用 HTTP2 后,像图片精灵之类的功能将不再必要。
我不确定情况是否会如此。我认为我们需要更多数据和更多测试用例。我肯定听到了相互矛盾的说法。我认为它不像“如果您使用 HTTP/2,就永远不要连接任何东西”那样简单。
我们主要使用两层方法。
通用图标/图片 SVG
对于大多数页面、菜单、徽标等上使用的图片,我们倾向于使用内联精灵文件。是的,它有一个小的构建过程,但对我们有一些优势:我们可以使用它从我们的 CMS 添加语言特定的可访问文本,而使用单个 SVG 文件似乎不太容易?
为了实现这种多语言/可访问文本的另一种方法可能是使用多个 svg 文件,例如
svg-en.svg
、svg-fr.svg
用于多种语言?或者也许在 svg 文件中使用
<switch>
,例如但是,使用
<switch>
似乎比在 CMS 中保存它更难维护?单个且使用较少的 SVG
对于单个或使用较少的单个 SVG 图片,比如插图或类似的东西,我们可以通过包含的方式加载它们,并以这种方式内联它们。在这种情况下,我们可能会使用语言切换。
有人对这种方法有什么想法或评论吗?我还想到,可能在首次启动时包含/内联 SVG 文件,然后将其存储在服务工作者中,并从缓存中提取它。但我不确定这是否有什么好处。
这是我们在生产中一直在使用的方法。它易于设置样式和动画。
值得一提的是,如果您在一个页面上多次使用相同的图标,Gzip 压缩将不再让您担心文件大小。这消除了
看起来很酷:) 但我经常需要 svg 作为背景图片,对于那些,现在做个精灵吧?