图标往往是组件库最先遇到的"基础设施"。本文讲图标体系的第一步——统一资源来源与接入策略,并落地三条主线:Iconify(主入口)、自定义 SVG 组件、IconFont 字体图标,最后用 Clipboard API 给图标列表加上一键复制能力,提升文档与设计协作效率。
vite-plugin-svg-icons + 统一 SvgIcon 组件接入自定义 SVG图标来源若不统一,很快会出现命名混乱、风格不一致、调用方式分裂。常见来源有三类:
无论来源如何,最终都应收口到同一个图标组件,调用方式、命名约定、渲染入口三件事尽早固定。
Iconify 用 前缀:图标名(如 ep:edit、mdi:home)统一调用,配合 UnoCSS 可直接 <div class="i-ep-edit" />。它最大的价值是"图标丰富 + 按需加载 + 调用统一",适合通用图标;业务特殊图标仍需自定义 SVG。
两个高频优化点:
loadIcons 在 onBeforeMount 预加载核心图标列表,加载后进入内存缓存。addAPIProvider('local', { resources: [...] }) 把图标 JSON 放到 public/icons 下,改用 @local:bi:home 形式访问,不再依赖网络。缓存层级上,Iconify 有内存缓存(最快)、LocalStorage(跨页持久)、Service Worker(PWA 离线)三层,首次走网络、之后命中缓存。
固定品牌或业务图标更适合本地 SVG。用 vite-plugin-svg-icons 把 src/assets/icons 下的 .svg 自动注册成 SVG Sprite,配合统一组件 SvgIcon 提供一致的 type / size / color API:
<template>
<svg :style="{ width: `${size}px`, height: `${size}px` }" aria-hidden="true">
<use :xlink:href="`#icon-${type}`" :fill="color" />
</svg>
</template>
<script setup lang="ts">
const props = defineProps({
type: { type: String, required: true },
size: { type: [Number, String], default: 16 },
color: { type: String, default: 'currentColor' }
})
</script>关键点:SVG 文件用 currentColor 而非硬编码颜色,使图标跟随文字颜色;main.ts 需导入 virtual:svg-icons-register 虚拟模块。注意该插件在处理 stroke 图标时偶有变形,优先选 fill 图标或用社区修复版。
IconFont 是 iconfont.cn 提供的字体图标方案,支持 Unicode / Font Class / Symbol 三种引用。把设计师图标接入时,同样包装进统一组件(如 IconFont 用 iconfont icon-${type} 类名),避免与 Iconify、自定义 SVG 三套写法并存。缺点是字体 URL 易硬编码、难本地化,适合已有 iconfont 项目资产的场景。
当图标很多时,复制名称、类名或 SVG 代码能显著降低使用门槛。核心是 navigator.clipboard.writeText:
const copyIcon = async (text: string) => {
await navigator.clipboard.writeText(text)
}复制内容应与真实使用方式一致(如 ep:edit 或完整 <svg> 片段),并为成功 / 失败提供反馈。图标展示项与复制按钮组合后,可直接服务文档页或图标选择器。
| 维度 | Iconify | 自定义 SVG | IconFont |
|---|---|---|---|
| 图标数量 | 20 万+ | 自定义 | 设计师上传 |
| 网络依赖 | CDN 模式需要 | 无 | 字体文件 |
| 自定义难度 | 较困难 | 容易 | 中等 |
| 推荐场景 | 图标变化多、需大量选择 | 图标固定、品牌图标 | 已有 iconfont 资产 |
经验策略:项目初期图标未定、需大量选择时用 Iconify;图标已固定或需品牌定制用自定义 SVG;已有 iconfont 项目资产时直接集成。
接入之后还有几件收尾工作容易被忽略,它们决定图标体系是"能用"还是"可交付"。
ep.json(含 collection 与 icons 数组),既作为列表组件数据源,也便于统计体积与做按需加载。viewBox + currentColor 的干净结构,避免颜色被锁死。import ep.json,需在 env.d.ts 补全 JSON 模块声明;内网场景则把本地图标源放到 public 以摆脱 CDN 依赖。把这些收尾标准化,图标体系就从零散技巧变成可维护的基础设施。
来源再多,调用端只认一套命名,混乱才不会发生。推荐四条收敛规则:
collection:name,自定义 SVG 用 icon- 前缀,IconFont 用 iconfont icon- 前缀,三者泾渭分明,不混写。export-order、user-avatar,避免中文或驼峰,方便在属性里直接书写与搜索。ep.json 与文档页移除,避免"文档有、代码无"的幽灵引用。命名约定看似小事,却是图标体系能否长期不腐化的底层纪律——它让三类来源在调用层彻底收敛成一套词表。
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 图标闪烁 | 异步从 CDN 加载 | 用 loadIcons 预加载 |
| 本地图标加载失败 | JSON 路径或 Provider 配置错 | 核对 public 路径与 addAPIProvider |
| SVG 颜色不变 | 文件硬编码颜色 | 改用 currentColor 或组件 color |
| stroke 图标变形 | 插件已知问题 | 用修复版或避免 stroke 图标 |
| 复制无效 | 非 HTTPS / 非 localhost | 在 localhost 或 HTTPS 下测试 |