Skip to content

SVG Sprite

SVG Sprite 是将多个 SVG 图标合并到单个文件中,通过 <symbol><use> 引用的技术。它可以减少 HTTP 请求、方便管理和维护图标资源,是现代 Web 开发中广泛使用的图标管理方案。本节将详细介绍 SVG Sprite 的制作和使用方法。

前置知识

阅读本节前,建议先了解:SVG 动画

基础概念

SVG Sprite 有两种主要实现方式:

方式说明优点缺点
内联 Sprite所有 symbol 放在 HTML 的一个 SVG 中无额外请求、可 CSS 控制增加 HTML 大小
外部 Spritesymbol 放在独立的 .svg 文件中HTML 简洁、可复用需要 HTTP 请求
雪碧图 (Sprite Sheet)多个图标拼接成一张大图传统方案兼容性好难以维护、需坐标计算

symbol 和 use

symbol 定义可复用图形

<symbol> 定义一个可复用的图形模板,它不会直接渲染,但可以通过 <use> 实例化。

use 引用 symbol

<use> 通过 hrefxlink: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>xy 偏移显示对应区域:

html
<!-- 雪碧图方式(传统) -->
<svg width="0" height="0" style="position: absolute;">
  <symbol id="icon-home" viewBox="0 0 24 24">
    <!-- 图标内容 -->
  </symbol>
</svg>

对比

特性内联 Symbol外部 SpriteSprite 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>

注意事项

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 文件
SVGOSVG 文件压缩优化
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 基础

参考链接