Skip to content
返回构建教程

图标系统

搭可 tree-shake 的图标系统,带 SVG 优化、一致尺寸和可访问性。

为什么

图标在产品中用 50+ 次,尺寸、描边宽或对齐不一致立即可见。正确图标系统把图标当组件:每 SVG 优化(无编辑器元数据)、可 tree-shake(仅用到的图标发布)、同 scale 尺寸(16/20/24px)、可访问(装饰 vs 有意义用 aria-label 或 title)。没有它,图标粘为原始 SVG 带不同描边宽和 viewBox,bundle 含 200 未用图标,屏幕阅读器报'图像'无标签。系统在你第一次换图标集时回本——一行变更而非 200 文件查找替换。

何时使用

有 ≥10 图标时。用 lucide-react 或自定义 sprite 约 1-2 小时。纯文本应用可跳过。

如何做

1. 用成熟库(lucide-react、phosphor-react)或建自定义集
   - lucide-react:1000+ 图标、可 tree-shake、一致 24px viewBox、描边
   - 自定义:SVGR 把 SVG 转 React 组件带优化

2. 标准化一个尺寸 scale:16、20、24px(1.5 倍比)
   - size 作 prop:<Icon size={20} />——永不每图标 className="w-5 h-5"
   - 默认 24px;密集 UI 用 20,行内文本用 16

3. 每图标接受 className 并展开 props
   - <Icon className="text-spark-500" /> 通过 currentColor 上色
   - 转发 ref 让 tooltip 可附图标

4. 区分装饰 vs 有意义图标供 a11y
   - 装饰(紧邻文本标签):aria-hidden="true"
   - 有意义(单独、传达信息):aria-label="关闭" 或 <title> 子

5. 加 Storybook 页网格渲染所有图标供视觉 QA
   - 易按名找图标并一眼查一致性

示例

// 用 lucide-react(可 tree-shake,仅用到图标发布)
import { Search, X, ChevronDown } from "lucide-react";
export function SearchBar() {
  return (
    <div className="relative">
      <Search size={20} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" aria-hidden />
      <input className="pl-9" />
      <X size={16} className="absolute right-3 top-1/2 -translate-y-1/2" aria-label="清除搜索" />
    </div>
  );
}

// 用 SVGR 自定义图标(svgr.config.js)
module.exports = { svgrConfig: { icon: true, replaceAttrValues: { "#000": "currentColor" } } };
// import Close from "./close.svg?react";  → <Close size={20} />

常见问题

  • ⚠导入整个图标库(import * as Icons)而非命名导入,发布 2MB 未用 SVG。
  • ⚠同 UI 图标用不同描边宽,看起来像拼写错。
  • ⚠装饰图标忘 aria-hidden,屏幕阅读器每词间报'图像'。
  • ⚠硬编码图标颜色而非用 currentColor,主题化要改每图标。

最佳实践

  • ✓仅命名导入(import { Search } from 'lucide-react')让打包器 tree-shake 未用图标。
  • ✓整图标集标准化一个描边宽(1.5 或 2)——一致性比具体值重要。
  • ✓图标填充/描边用 currentColor,颜色由 text-* 类控,主题化自动工作。
  • ✓自定义 SVG 跑 SVGO 去编辑器元数据(Inkscape 命名空间、空组)——减尺寸 40-60%。