React SVG 最佳实践:所有实现方法完整指南

在 React 中使用 SVG 的综合指南 - 比较所有方法,包括 img 标签、内联 SVG、SVGR、图标库和动态导入,附带优缺点和实际示例

React SVG 最佳实践:所有实现方法完整指南

SVG 对于现代 React 应用至关重要,但有多种实现方式 - 每种都有不同的权衡。本综合指南将探索所有方法,比较它们的性能,并提供实际示例帮助您选择最佳方法。

React 中使用 SVG 的 5 种方法

方法 1: <img> 标签

实现:

import logoUrl from './logo.svg'
 
function Logo() {
  return <img src={logoUrl} alt="公司徽标" className="w-48 h-12" />
}

优点:

  • ✅ 最简单的方法
  • ✅ 浏览器缓存
  • ✅ 懒加载支持
  • ✅ 不增加包体积

缺点:

  • ❌ 无法使用 CSS 样式
  • ❌ 无法使用 JavaScript 控制
  • ❌ 额外的 HTTP 请求

适用场景: 静态徽标、大型插图、装饰性图像


方法 2: 内联 SVG

实现:

function HomeIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6">
      <path d="M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69..." />
    </svg>
  )
}

优点:

  • ✅ 完全的 CSS 控制
  • ✅ JavaScript 操作
  • ✅ 主题支持(currentColor)
  • ✅ 完整的无障碍访问

缺点:

  • ❌ 增加包体积
  • ❌ 代码冗长
  • ❌ 难以维护

适用场景: 小图标(< 1 KB)、动画 SVG、交互式 SVG


方法 3: SVGR(组件导入)

配置 vite.config.ts:

import svgr from 'vite-plugin-svgr'
 
export default defineConfig({
  plugins: [
    react(),
    svgr({
      svgrOptions: {
        icon: true,
        typescript: true,
      },
    }),
  ],
})

使用:

import { ReactComponent as Logo } from './logo.svg'
 
function Header() {
  return <Logo className="w-32 h-8 text-blue-600" />
}

优点:

  • ✅ 两全其美 - 文件分离 + 完全控制
  • ✅ 自动优化
  • ✅ TypeScript 支持
  • ✅ Props 支持

缺点:

  • ❌ 需要构建配置
  • ❌ 增加包体积

适用场景: 可重用组件、TypeScript 项目、设计系统


方法 4: 图标组件库

流行的库:

npm install @heroicons/react
npm install lucide-react
npm install @phosphor-icons/react

Heroicons 示例:

import { HomeIcon } from '@heroicons/react/24/outline'
 
function Navigation() {
  return <HomeIcon className="w-6 h-6 text-gray-600" />
}

Lucide 示例:

import { Heart, Star } from 'lucide-react'
 
function ProductCard() {
  return (
    <div>
      <Heart size={24} color="red" fill="pink" />
      <Star size={20} fill="gold" />
    </div>
  )
}

优点:

  • ✅ 零配置
  • ✅ 一致的设计
  • ✅ TypeScript 支持
  • ✅ Tree shaking

缺点:

  • ❌ 增加包体积
  • ❌ 外部依赖
  • ❌ 有限的自定义

适用场景: 快速原型、一致的设计系统、常见 UI 模式


方法 5: 动态 SVG 加载

实现:

function DynamicSVG({ name }: { name: string }) {
  const [svg, setSvg] = useState<string>('')
  
  useEffect(() => {
    import(`./icons/${name}.svg?raw`)
      .then(module => setSvg(module.default))
  }, [name])
  
  return <div dangerouslySetInnerHTML={{ __html: svg }} />
}

优点:

  • ✅ 按需加载
  • ✅ 减少初始包体积
  • ✅ 动态内容

缺点:

  • ❌ 复杂性高
  • ❌ 类型安全困难
  • ❌ XSS 风险

适用场景: CMS 驱动内容、插件系统、非常大的图标集


比较表

方法包体积样式TypeScript缓存设置最适合
<img>✅ 无❌ 否⚠️ 有限✅ 是✅ 简单静态徽标
内联 SVG❌ 增加✅ 完全✅ 是❌ 否✅ 简单小图标
SVGR❌ 增加✅ 完全✅ 完全❌ 否⚠️ 需配置组件
图标库⚠️ 每个图标✅ 好✅ 完全❌ 否✅ 简单快速开发
动态加载✅ 最小⚠️ 有限❌ 困难✅ 自定义❌ 复杂CMS 内容

使用场景推荐

小型项目 / 落地页

使用: 图标库(Heroicons 或 Lucide)

设计系统 / 组件库

使用: SVGR 用于自定义图标

大型应用

使用: 组合方式

  • 图标: 图标库
  • 徽标: <img> 标签
  • 自定义插图: SVGR

性能关键应用

使用: <img> 标签 + 懒加载

性能优化技巧

1. 懒加载图标

const DashboardIcons = lazy(() => import('./icons/dashboard'))

2. 使用 SVG 精灵图

<svg className="w-6 h-6">
  <use href="/sprite.svg#home" />
</svg>

3. 优化 SVG 文件

npx svgo icon.svg -o icon.optimized.svg

4. Tree Shaking

// ✅ 好: 命名导入
import { HomeIcon } from '@heroicons/react/24/outline'
 
// ❌ 坏: 命名空间导入
import * as Icons from '@heroicons/react/24/outline'

无障碍访问最佳实践

装饰性图标

<button>
  <HomeIcon aria-hidden="true" className="w-5 h-5" />
  <span>主页</span>
</button>

独立图标

<button aria-label="转到主页">
  <HomeIcon className="w-6 h-6" />
</button>

快速决策指南

选择 <img> 如果:

  • ✅ 大型 SVG 文件(> 10 KB)
  • ✅ 不需要颜色/样式更改
  • ✅ 静态徽标或插图

选择内联 SVG 如果:

  • ✅ 非常小的图标(< 500 字节)
  • ✅ 需要 CSS 动画
  • ✅ 一次性自定义图标

选择 SVGR 如果:

  • ✅ 构建设计系统
  • ✅ 需要 TypeScript 支持
  • ✅ 可重用的自定义图标

选择图标库如果:

  • ✅ 快速开发
  • ✅ 标准 UI 图标
  • ✅ 不想管理图标

最终推荐

对于 2025 年的大多数现代 React 应用:

  1. 图标库(Heroicons/Lucide)用于标准 UI 图标
  2. SVGR 用于自定义品牌图标和插图
  3. <img> 标签用于大型装饰性 SVG

这种组合提供了开发体验、性能和灵活性的最佳平衡。

祝编码愉快!