SVG Sprite
SVG Sprite 是将多个 SVG 图标合并到单个文件中,通过 <symbol> 和 <use> 引用的技术。它可以减少 HTTP 请求、方便管理和维护图标资源,是现代 Web 开发中广泛使用的图标管理方案。本节将详细介绍 SVG Sprite 的制作和使用方法。
前置知识
阅读本节前,建议先了解:SVG 动画
基础概念
SVG Sprite 有两种主要实现方式:
| 方式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 内联 Sprite | 所有 symbol 放在 HTML 的一个 SVG 中 | 无额外请求、可 CSS 控制 | 增加 HTML 大小 |
| 外部 Sprite | symbol 放在独立的 .svg 文件中 | HTML 简洁、可复用 | 需要 HTTP 请求 |
| 雪碧图 (Sprite Sheet) | 多个图标拼接成一张大图 | 传统方案兼容性好 | 难以维护、需坐标计算 |
symbol 和 use
symbol 定义可复用图形
<symbol> 定义一个可复用的图形模板,它不会直接渲染,但可以通过 <use> 实例化。
use 引用 symbol
<use> 通过 href 或 xlink:href 引用已定义的 symbol 或其他元素。
基本用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG Sprite</title>
<style>
.icon { width: 24px; height: 24px; fill: currentColor; }
.icon-lg { width: 48px; height: 48px; }
svg { border: 1px solid #ccc; display: block; margin: 10px 0; }
</style>
</head>
<body>
<h1>SVG Sprite 示例</h1>
<!-- 定义 Sprite(通常放在 body 开头或通过 JS 注入) -->
<svg style="display: none;" xmlns="http://www.w3.org/2000/svg">
<!-- 首页图标 -->
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</symbol>
<!-- 搜索图标 -->
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</symbol>
<!-- 用户图标 -->
<symbol id="icon-user" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</symbol>
<!-- 设置图标 -->
<symbol id="icon-settings" viewBox="0 0 24 24">
<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 1 1 12 8.4a3.6 3.6 0 0 1 0 7.2z"/>
</symbol>
</svg>
<!-- 使用图标 -->
<svg width="500" height="200" viewBox="0 0 500 200">
<!-- 基本使用 -->
<use href="#icon-home" x="20" y="20" width="48" height="48"/>
<!-- 指定位置和尺寸 -->
<use href="#icon-search" x="100" y="20" width="48" height="48"/>
<!-- 带颜色 -->
<use href="#icon-user" x="180" y="20" width="48" height="48" fill="#ea4335"/>
<!-- 带描边 -->
<use href="#icon-settings" x="260" y="20" width="48" height="48"
fill="none" stroke="#4285f4" stroke-width="1"/>
<!-- 小尺寸 -->
<use href="#icon-home" x="340" y="20" width="24" height="24"/>
<use href="#icon-search" x="370" y="20" width="24" height="24"/>
<use href="#icon-user" x="400" y="20" width="24" height="24"/>
<use href="#icon-settings" x="430" y="20" width="24" height="24"/>
<!-- 标签 -->
<text x="44" y="90" font-size="12" text-anchor="middle" fill="#333">首页</text>
<text x="124" y="90" font-size="12" text-anchor="middle" fill="#333">搜索</text>
<text x="204" y="90" font-size="12" text-anchor="middle" fill="#ea4335">用户</text>
<text x="284" y="90" font-size="12" text-anchor="middle" fill="#4285f4">设置</text>
<!-- 按钮示例 -->
<rect x="20" y="120" width="120" height="40" rx="8" fill="#4285f4"/>
<use href="#icon-home" x="40" y="130" width="20" height="20" fill="white"/>
<text x="80" y="145" font-size="14" fill="white" text-anchor="middle">首页</text>
</svg>
</body>
</html>SVG Sprite 文件制作
外部 Sprite 文件
创建一个 icons.svg 文件,包含所有图标定义:
html
<!-- icons.svg -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</symbol>
<symbol id="icon-user" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</symbol>
<!-- 更多图标... -->
</svg>在 HTML 中引用外部 Sprite
html
<!-- 方式 1:直接引用 -->
<svg width="24" height="24">
<use href="icons.svg#icon-home"></use>
</svg>
<!-- 方式 2:通过 JavaScript 注入(推荐) -->
<script>
// 注入 sprite 到页面
fetch('icons.svg')
.then(res => res.text())
.then(data => {
const div = document.createElement('div');
div.innerHTML = data;
div.style.display = 'none';
document.body.prepend(div);
});
</script>方式 2 中的 CSS 样式控制
html
<style>
.icon {
display: inline-block;
width: 1em;
height: 1em;
fill: currentColor;
vertical-align: -0.125em;
}
</style>
<!-- 使用:图标大小随文字变化 -->
<span class="icon"><use href="#icon-home"/></span>
<span style="font-size: 32px;" class="icon"><use href="#icon-home"/></span>雪碧图 (Sprite Sheet) vs 内联 Symbol
传统 Sprite Sheet
将多个图标拼接成一张大图,通过 <use> 的 x、y 偏移显示对应区域:
html
<!-- 雪碧图方式(传统) -->
<svg width="0" height="0" style="position: absolute;">
<symbol id="icon-home" viewBox="0 0 24 24">
<!-- 图标内容 -->
</symbol>
</svg>对比
| 特性 | 内联 Symbol | 外部 Sprite | Sprite Sheet |
|---|---|---|---|
| HTTP 请求 | 无 | 1 个 | 1 个 |
| HTML 大小 | 增加 | 小 | 小 |
| CSS 可控 | 是 | 是 | 有限 |
| 动画支持 | 是 | 是 | 难 |
| 缓存 | 无 | 有 | 有 |
| 维护难度 | 中 | 低 | 高 |
| 图标着色 | 容易 | 容易 | 困难 |
| 推荐度 | 高 | 最高 | 低 |
实战示例
示例:导航栏图标
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG Sprite 导航栏</title>
<style>
body { font-family: sans-serif; margin: 0; padding: 20px; }
/* Sprite 定义(隐藏) */
.svg-sprite { display: none; }
/* 图标通用样式 */
.nav-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
color: #666;
cursor: pointer;
transition: all 0.2s;
border-radius: 12px;
}
.nav-icon:hover {
background: rgba(66, 133, 244, 0.1);
color: #4285f4;
}
.nav-icon.active {
background: rgba(66, 133, 244, 0.15);
color: #4285f4;
}
.nav-icon svg {
width: 24px;
height: 24px;
}
.nav-label {
font-size: 11px;
margin-top: 4px;
text-align: center;
}
/* 导航栏 */
.navbar {
display: flex;
justify-content: space-around;
max-width: 400px;
margin: 0 auto;
padding: 16px;
background: #f5f5f5;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
</style>
</head>
<body>
<h1>SVG Sprite 导航栏</h1>
<!-- Sprite 定义 -->
<svg class="svg-sprite" xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</symbol>
<symbol id="icon-explore" viewBox="0 0 24 24">
<path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"/>
</symbol>
<symbol id="icon-favorite" viewBox="0 0 24 24">
<path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/>
</symbol>
<symbol id="icon-person" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</symbol>
</svg>
<!-- 导航栏 -->
<div class="navbar">
<div class="nav-item">
<div class="nav-icon active">
<svg><use href="#icon-home"/></svg>
</div>
<span class="nav-label" style="color: #4285f4;">首页</span>
</div>
<div class="nav-item">
<div class="nav-icon">
<svg><use href="#icon-explore"/></svg>
</div>
<span class="nav-label">发现</span>
</div>
<div class="nav-item">
<div class="nav-icon">
<svg><use href="#icon-favorite"/></svg>
</div>
<span class="nav-label">收藏</span>
</div>
<div class="nav-item">
<div class="nav-icon">
<svg><use href="#icon-person"/></svg>
</div>
<span class="nav-label">我的</span>
</div>
</div>
</body>
</html>注意事项
1. href vs xlink:href
html
<!-- 推荐:现代写法 -->
<use href="#icon-home"/>
<!-- 兼容旧浏览器(IE) -->
<use xlink:href="#icon-home" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<!-- 同时提供两者以确保最大兼容性 -->
<use href="#icon-home" xlink:href="#icon-home"/>2. symbol 的 viewBox
所有 symbol 都应该设置 viewBox,确保图标在不同尺寸下正确缩放:
html
<!-- 正确:带 viewBox -->
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="..."/>
</symbol>
<!-- 错误:缺少 viewBox,图标可能变形 -->
<symbol id="icon-home">
<path d="..."/>
</symbol>3. use 元素的样式继承
<use> 创建的 Shadow DOM 会继承父元素的颜色,但不会继承字体等属性:
html
<!-- 通过 CSS 控制颜色 -->
<style>
.icon-red { color: #ea4335; }
.icon-blue { color: #4285f4; }
</style>
<svg class="icon-red"><use href="#icon-home"/></svg>
<svg class="icon-blue"><use href="#icon-home"/></svg>最佳实践
1. 图标设计规范
| 规范 | 说明 |
|---|---|
| viewBox | 统一使用 0 0 24 24 |
| 填充 | 使用 currentColor 以支持 CSS 着色 |
| 描边 | 默认无描边,或使用 stroke="currentColor" |
| 间距 | 四周留 1-2px 间距 |
| 命名 | 使用语义化前缀 icon- |
| 尺寸 | 路径基于 24px 设计 |
2. 自动化工具
推荐使用工具自动管理 SVG Sprite:
| 工具 | 用途 |
|---|---|
svg-sprite (Node.js) | 自动生成 Sprite 文件 |
| SVGO | SVG 文件压缩优化 |
| Figma/Sketch 插件 | 从设计稿导出 SVG |
svgstore (Gulp/Webpack) | 构建时自动合并 |
3. Webpack/Vite 集成
javascript
// Vite 配置:自动导入 SVG Sprite
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';
export default {
plugins: [
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
symbolId: 'icon-[name]',
}),
],
};html
<!-- 自动注入后直接使用 -->
<svg><use href="#icon-home"/></svg>下一节
继续学习:AudioContext 基础