SVG Symbol
字体图标的替代方案——使用 SVG symbol 精灵将可缩放的多色图标直接嵌入 HTML。
什么是 SVG Symbol 精灵?
SVG symbol 精灵将多个 SVG 图标打包到一个文件中。每个图标定义在一个带有唯一 id 的 <symbol> 元素内。要使用某个图标,通过 <use href="#icon-id"> 引用它。精灵文件只加载一次——无论是内联还是作为外部文件——各个图标即可在页面任意位置渲染。
这与字体图标是根本不同的方式。它不是将 Unicode 码位映射到字体字形,而是使用原生 SVG 元素。每个图标保留了完整的 SVG 能力:多色、渐变、滤镜和细粒度的无障碍属性。代价是相比字体图标的纯 CSS 方式,标记和样式约定稍显复杂。
工作原理
精灵文件是一个包含一个或多个 <symbol> 元素的普通 SVG 文档。每个 symbol 定义一个带有自己 viewBox 的独立图标:
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M3 12l9-9 9 9M5 10v10a1 1 0 001 1h3a1 1 0 001-1v-4..."/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</symbol>
</svg>
外层 <svg> 用 style="display:none" 隐藏,不占用页面空间。内部的 symbol 在被引用之前不可见。要渲染一个图标,使用 <use> 元素:
<svg class="icon"><use href="#icon-home"></use></svg>
<svg class="icon"><use href="#icon-search"></use></svg>
用 CSS 控制图标样式,使用 fill 和 stroke 而非字体图标的 color 属性:
.icon {
width: 24px;
height: 24px;
fill: currentColor;
stroke: none;
}
使用 fill: currentColor 让图标继承父元素的文字颜色,与字体图标通过 color 属性控制颜色的方式类似。
Symbol 与字体图标对比
- 支持多色——每条路径可以有独立的填充和描边
- 每个图标是真正的 SVG 元素,无障碍性更好
- 无需 Unicode 映射——通过可读名称引用图标
- 渲染更清晰——没有字体微调问题或次像素伪影
- 各图标部件可以独立动画
- 支持所有 SVG 功能:渐变、滤镜、裁剪路径、蒙版
- 标记更冗长(
<svg><use>对比<i class="icon">) - 难以用 CSS
color设置样式——需要fill/stroke - 外部精灵有跨域加载的 CORS 问题
- 总文件大小比压缩字体格式更大
- 每次使用图标的 HTML 体积更大
- 不支持
::before/::after伪元素
加载策略
将 SVG symbol 精灵加载到页面中有三种主要方式,各有不同的权衡:
内联精灵
将整个精灵 SVG 直接粘贴到 HTML <body> 中。这是最简单的方式——无 CORS 问题,无额外 HTTP 请求。所有 symbol 立即可供页面上任何 <use> 引用。最适合单页应用或图标集较小(少于约 50 个图标)的场景。
外部精灵
通过 <use href="icons.svg#home"> 将精灵作为外部文件加载。这可以保持 HTML 简洁,并让浏览器单独缓存精灵文件。但有 CORS 问题:精灵必须从同源提供,或者服务器必须设置适当的 Access-Control-Allow-Origin 响应头。注意 Internet Explorer 不支持外部 <use> 引用——如需 IE 支持,请使用 svg4everybody polyfill。
JS 注入
通过 fetch() 加载精灵文件并在运行时注入 DOM。这兼具外部精灵的可缓存性和内联精灵的可靠性——注入的 SVG 成为文档的一部分,<use> 引用无需 CORS 限制即可工作。Bobcorn 生成的正是这种方式:一个在加载时获取并注册所有 symbol 的 JS 文件。
<!-- 加载生成的 JS 精灵 -->
<script src="icons-symbol.js"></script>
<!-- 然后正常使用: -->
<svg class="icon"><use href="#icon-home"></use></svg>
<svg class="icon"><use href="#icon-search"></use></svg>
JS 文件创建一个包含所有 <symbol> 定义的隐藏 <svg> 元素并附加到文档 body。加载完成后,图标的引用方式与内联方式完全相同。
何时使用 SVG Symbol
当项目需要字体图标无法提供的能力时,SVG symbol 精灵是正确选择:
- 多色图标——当图标使用多种颜色、渐变或单字形字体无法表示的复杂填充时
- 无障碍性是首要考量——每个图标可以包含
<title>元素和aria-label属性,为屏幕阅读器提供有意义的描述 - 图标局部动画——需要对图标内部的单条路径或群组进行动画(例如,设置图标中旋转的齿轮)
- 大量使用 SVG 的项目——应用已广泛使用内联 SVG,symbol 可以自然地融入现有架构
- 最大渲染清晰度——字体微调在小尺寸下可能导致对齐问题;SVG symbol 在任何尺寸下都能像素级完美渲染
何时坚持使用字体图标
在几种常见场景中,字体图标仍然是更好的选择:
- 大型图标集(200+)——压缩的 WOFF2 字体文件比包含相同数量图标的 SVG 精灵小得多
- 全部为单色图标——如果每个图标都是单色,字体图标提供最简单的集成,无需妥协
- 仅 CSS 集成——字体图标只需一个样式表链接和 CSS 类,无需 JavaScript 或额外标记
- 遗留系统——已使用字体图标的项目,迁移收益不足以证明切换的合理性
- 伪元素支持——需要在
::before或::after伪元素中使用图标,而这只有字体字形才能做到