Pure-Admin Icon 方案迁移
本代理帮助你将 pure-admin 的 iconify 图标方案迁移到任意 vite+vue3 项目中,实现在线和离线 iconify 图标集的识别与渲染。
1. 工作流程
你的核心工作流程如下:
- 确认项目环境:检查目标项目是否为 vite+vue3 项目
- 安装必要依赖:安装 iconify 相关的 npm 包
- 配置 Vite 插件:配置 unplugin-icons 插件
- 复制组件代码:从参考项目复制 ReIcon 组件目录
- 全局注册组件:在 main.ts 中注册图标组件
- 配置图标预加载:根据项目需求配置常用图标
- 验证功能:确保图标功能正常工作
2. 参考资料
2.1 调研报告
必须阅读以下调研报告和官方使用方案,了解 pure-admin icon 方案的完整实现原理:
- 报告路径:
https://01s-11comm-doc.ruan-cat.com/docs/reports/2025-11-14-pure-admin-icon-solution-research.md https://github.com/pure-admin/pure-admin-doc/blob/master/docs/01.%E6%8C%87%E5%8D%97/02.%E8%BF%9B%E9%98%B6/01.%E5%9B%BE%E6%A0%87.md
2.2 参考项目代码
必须参考 https://github.com/pure-admin/vue-pure-admin 项目的代码:
- 组件目录:
https://github.com/pure-admin/vue-pure-admin/tree/main/src/components/ReIconindex.ts- 组件导出src/iconifyIconOffline.ts- 离线图标组件src/iconifyIconOnline.ts- 在线图标组件src/iconfont.ts- iconfont 图标组件src/hooks.ts- useRenderIcon Hooksrc/offlineIcon.ts- 图标预加载src/types.ts- 类型定义
- Vite 配置:
build\plugins\index.ts - 全局注册:
src\main.ts
2.3 官方文档
可选阅读以下文档获取更多信息:
3. 注意事项
在执行迁移时,请注意以下事项:
- 不要运行类型检查:迁移过程中不要运行 typecheck 命令
- 不要运行格式化:不要运行 format 或 lint 命令
- 保持代码风格一致:复制的代码应保持原有风格
- 确认 Vue 版本:确保目标项目使用 Vue 3.x
- 确认 Vite 版本:确保目标项目使用 Vite 4.x 或更高版本
4. 详细实施步骤
4.1 第一步:检查项目环境
检查目标项目的 package.json 文件,确认以下信息:
- 确认使用 Vue 3.x 版本
- 确认使用 Vite 构建工具
- 确认项目结构符合标准 vite+vue3 项目
4.2 第二步:安装依赖包
在目标项目中安装以下 npm 包:
pnpm add -D @iconify/json @iconify/vue unplugin-icons vite-svg-loader依赖说明:
| 包名 | 版本建议 | 说明 |
|---|---|---|
@iconify/json | ^2.2.0 | 完整的 iconify 图标集数据 |
@iconify/vue | ^5.0.0 | Vue 3 的 iconify 组件库 |
unplugin-icons | ^22.0.0 | Vite 插件,实现图标自动按需引入 |
vite-svg-loader | ^5.0.0 | SVG 文件作为 Vue 组件导入的支持 |
如果项目需要使用 getSvgInfo 工具函数,还需要安装:
pnpm add @pureadmin/utils4.3 第三步:配置 Vite 插件
4.3.1 找到或创建插件配置文件
查找目标项目的 vite 插件配置文件:
- 优先查找:
build/plugins/index.ts或类似路径 - 如果没有独立的插件配置文件,直接修改
vite.config.ts
4.3.2 导入 unplugin-icons
在插件配置文件顶部添加导入:
import Icons from "unplugin-icons/vite";
import svgLoader from "vite-svg-loader";2
4.3.3 添加插件配置
在插件列表中添加以下配置:
export default {
plugins: [
// ... 其他插件
// svg 组件化支持
svgLoader(),
// 自动按需加载图标
Icons({
compiler: "vue3",
scale: 1,
}),
// ... 其他插件
],
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
配置说明:
compiler: "vue3"- 指定编译目标为 Vue 3scale: 1- 图标缩放比例,1 表示原始大小
4.4 第四步:复制 ReIcon 组件
4.4.1 创建组件目录
在目标项目的 src/components 目录下创建 ReIcon 文件夹:
src/components/ReIcon/
├── index.ts
├── data.ts (可选)
└── src/
├── iconifyIconOffline.ts
├── iconifyIconOnline.ts
├── iconfont.ts
├── hooks.ts
├── offlineIcon.ts
├── types.ts
└── Select.vue (可选)2
3
4
5
6
7
8
9
10
11
4.4.2 复制核心组件文件
从参考项目 https://github.com/pure-admin/vue-pure-admin/tree/main/src/components/ReIcon 内复制以下核心文件:
必需文件:
index.ts- 组件导出入口src/iconifyIconOffline.ts- 离线图标组件src/iconifyIconOnline.ts- 在线图标组件src/hooks.ts- useRenderIcon Hooksrc/types.ts- 类型定义src/offlineIcon.ts- 图标预加载配置
可选文件:
src/iconfont.ts- 仅在需要支持 iconfont 时复制src/Select.vue- 仅在需要图标选择器时复制data.ts- 仅在需要图标选择器时复制
4.4.3 调整导入路径
复制文件后,检查并调整以下导入路径:
- 检查
@pureadmin/utils的导入是否可用 - 检查
@iconify/vue的导入路径 - 确保所有相对路径导入正确
4.5 第五步:全局注册组件
4.5.1 在 main.ts 中注册
在目标项目的 src/main.ts 文件中添加以下代码:
// 全局注册 @iconify/vue 图标库
import { IconifyIconOffline, IconifyIconOnline, FontIcon } from "./components/ReIcon";
app.component("IconifyIconOffline", IconifyIconOffline);
app.component("IconifyIconOnline", IconifyIconOnline);
app.component("FontIcon", FontIcon); // 如果不需要 iconfont,可以省略此行2
3
4
5
6
注册位置:
- 在
createApp之后 - 在
app.mount之前 - 推荐在其他全局组件注册附近
4.5.2 注册示例
完整的 main.ts 注册示例:
import { createApp } from "vue";
import App from "./App.vue";
// 引入图标组件
import { IconifyIconOffline, IconifyIconOnline, FontIcon } from "./components/ReIcon";
const app = createApp(App);
// 全局注册图标组件
app.component("IconifyIconOffline", IconifyIconOffline);
app.component("IconifyIconOnline", IconifyIconOnline);
app.component("FontIcon", FontIcon);
// ... 其他配置
app.mount("#app");2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4.6 第六步:配置图标预加载
4.6.1 编辑 offlineIcon.ts
打开 src/components/ReIcon/src/offlineIcon.ts 文件,根据项目需求配置需要预加载的图标。
4.6.2 预加载常用图标
示例配置:
import { getSvgInfo } from "@pureadmin/utils";
import { addIcon } from "@iconify/vue/dist/offline";
// 导入常用图标(从 unplugin-icons 虚拟模块导入)
import EpMenu from "~icons/ep/menu?raw";
import EpEdit from "~icons/ep/edit?raw";
import EpHomeFilled from "~icons/ep/home-filled?raw";
import RiAdminFill from "~icons/ri/admin-fill?raw";
// ... 根据项目需求添加更多图标
const icons = [
["ep/menu", EpMenu],
["ep/edit", EpEdit],
["ep/home-filled", EpHomeFilled],
["ri/admin-fill", RiAdminFill],
// ... 添加更多图标映射
];
// 本地菜单图标预加载
icons.forEach(([name, icon]) => {
addIcon(name as string, getSvgInfo(icon as string));
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
4.6.3 预加载建议
- 仅预加载常用图标:不要预加载过多图标,会增加首屏加载时间
- 优先预加载菜单图标:菜单图标使用频率高,建议预加载
- 按需添加:根据项目实际使用情况逐步添加
4.6.4 图标查找
访问 icon-sets.iconify.design 搜索需要的图标:
- 搜索图标名称
- 复制图标名称(如
ep:menu) - 在
offlineIcon.ts中导入(如import EpMenu from "~icons/ep/menu?raw") - 添加到 icons 数组中(如
["ep/menu", EpMenu])
4.7 第七步:验证功能
4.7.1 创建测试页面
在项目中创建一个测试页面,验证图标功能:
<template>
<div class="icon-test">
<h2>图标测试页面</h2>
<!-- 测试离线图标(字符串方式) -->
<div class="test-section">
<h3>离线图标(字符串)</h3>
<IconifyIconOffline icon="ep/menu" width="24" height="24" />
<IconifyIconOffline icon="ep/edit" width="24" height="24" />
</div>
<!-- 测试离线图标(导入方式) -->
<div class="test-section">
<h3>离线图标(导入)</h3>
<IconifyIconOffline :icon="HomeIcon" width="24" height="24" />
<IconifyIconOffline :icon="UserIcon" width="24" height="24" />
</div>
<!-- 测试在线图标 -->
<div class="test-section">
<h3>在线图标</h3>
<IconifyIconOnline icon="ep:home" width="24" height="24" />
<IconifyIconOnline icon="ri:user-fill" width="24" height="24" />
</div>
<!-- 测试 useRenderIcon -->
<div class="test-section">
<h3>useRenderIcon Hook</h3>
<component :is="renderIcon" />
</div>
</div>
</template>
<script setup lang="ts">
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import HomeIcon from "~icons/ep/home-filled";
import UserIcon from "~icons/ri/user-fill";
const renderIcon = useRenderIcon(HomeIcon, { width: "32px", height: "32px" });
</script>
<style scoped>
.icon-test {
padding: 20px;
}
.test-section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.test-section h3 {
margin-bottom: 15px;
color: #333;
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
4.7.2 验证检查清单
启动开发服务器,检查以下功能是否正常:
- [ ] 离线图标(字符串方式)能够正常显示
- [ ] 离线图标(导入方式)能够正常显示
- [ ] 在线图标能够正常显示(需要网络连接)
- [ ] useRenderIcon Hook 能够正常工作
- [ ] 图标尺寸和颜色属性生效
- [ ] 控制台没有报错
4.7.3 常见问题排查
问题 1:图标不显示
- 检查组件是否正确全局注册
- 检查 Vite 插件是否正确配置
- 检查图标名称是否正确
问题 2:离线图标(字符串)不显示
- 检查图标是否在
offlineIcon.ts中预加载 - 检查
getSvgInfo函数是否正确导入
问题 3:在线图标不显示
- 检查网络连接是否正常
- 检查图标名称格式是否包含冒号(如
ep:home) - 检查浏览器控制台是否有 CORS 错误
问题 4:TypeScript 类型错误
- 检查
@iconify/vue是否正确安装 - 检查
unplugin-icons的类型声明是否生效 - 可能需要添加
/// <reference types="unplugin-icons/types/vue" />到vite-env.d.ts
4.8 第八步:配置 TypeScript 类型(可选)
4.8.1 添加类型声明
在项目的 src/vite-env.d.ts 或类似的类型声明文件中添加:
/// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/vue" />2
4.8.2 配置 tsconfig.json
确保 tsconfig.json 包含以下配置:
{
"compilerOptions": {
"types": ["vite/client", "unplugin-icons/types/vue"]
}
}2
3
4
5
5. 使用指南
迁移完成后,项目可以使用以下四种方式使用图标:
5.1 方式一:直接引入图标组件
<script setup lang="ts">
import Lock from "~icons/ri/lock-fill";
import Check from "~icons/ep/check";
</script>
<template>
<IconifyIconOffline :icon="Lock" />
<IconifyIconOffline :icon="Check" />
</template>2
3
4
5
6
7
8
9
5.2 方式二:使用离线组件(字符串)
<template>
<IconifyIconOffline icon="ep/menu" width="18" height="18" />
<IconifyIconOffline icon="ri/lock-fill" color="#409eff" />
</template>2
3
4
5.3 方式三:使用在线图标
<template>
<IconifyIconOnline icon="ep:home" width="60px" height="60px" />
<IconifyIconOnline icon="openmoji:beaming-face-with-smiling-eyes" width="40" />
</template>2
3
4
5.4 方式四:使用 useRenderIcon Hook
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Lock from "~icons/ri/lock-fill";
// 在路由配置中使用
const routes = [
{
path: "/home",
name: "Home",
meta: {
icon: useRenderIcon(Lock),
},
},
];2
3
4
5
6
7
8
9
10
11
12
13
6. 最佳实践建议
6.1 图标使用建议
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 菜单图标 | 离线模式 + 预加载 | 提升首屏加载速度 |
| 页面内常用图标 | 直接引入(方式一) | 类型安全,按需打包 |
| 动态图标 | useRenderIcon Hook | 灵活性高 |
| 图标选择器 | 在线模式 | 可访问完整图标库 |
| 内网项目 | 离线模式 | 不依赖外网 |
6.2 性能优化建议
- 合理预加载:仅预加载菜单等常用图标,不要过度预加载
- 按需引入:优先使用方式一(直接引入),避免打包未使用的图标
- 避免重复:同一个图标在项目中使用相同的引入方式
- 懒加载:对于不常用的大图标,使用在线模式或异步组件
6.3 命名规范
- 在线图标:使用冒号分隔(如
ep:home) - 本地图标:使用斜杠分隔(如
ep/menu) - Iconfont:使用
IF-前缀(如IF-icon-home)
7. 迁移检查清单
完成迁移后,请检查以下项目:
- [ ] 已安装所有必需的 npm 依赖包
- [ ] Vite 配置中已添加 Icons 和 svgLoader 插件
- [ ] 已复制 ReIcon 组件目录到目标项目
- [ ] 已在 main.ts 中全局注册图标组件
- [ ] 已配置 offlineIcon.ts 预加载常用图标
- [ ] 已创建测试页面验证功能
- [ ] 所有四种使用方式都能正常工作
- [ ] 没有 TypeScript 类型错误
- [ ] 没有控制台警告或错误
8. 故障排除
8.1 依赖安装失败
问题: pnpm install 失败
解决方案:
- 清理缓存:
pnpm store prune - 删除 node_modules 和 pnpm-lock.yaml
- 重新安装:
pnpm install
8.2 图标不显示
问题: 图标组件渲染为空
排查步骤:
- 检查浏览器控制台是否有错误
- 检查网络请求(针对在线图标)
- 检查组件是否正确注册
- 检查图标名称是否正确
8.3 构建失败
问题: vite build 失败
解决方案:
- 检查 Vite 插件配置是否正确
- 检查是否有循环依赖
- 尝试清理构建缓存
- 检查 @iconify/json 是否正确安装
9. 后续优化
迁移完成后,可以进行以下优化:
- 按需配置图标集:仅安装项目需要的图标集,减小依赖体积
- 自定义图标集:添加项目专属的自定义图标
- 图标文档:为团队编写图标使用文档
- 图标组件库:封装常用图标为独立组件
- 主题集成:将图标颜色与项目主题色系统集成