Iconify 是一套面向开发人员和设计师的图标工具生态,它以统一的方式整合了200 多个开源图标集 、超过 27.5 万个图标,是前端开发中极为常用的开源图标库。
但 Iconify 官方 API 服务部署在国外,国内访问时经常出现网络波动、加载缓慢甚至失败的情况。为了保证项目中图标使用的稳定性,我选择自建 Iconify API 服务,以此获得更流畅的开发和使用体验。
先明确:你的项目是如何使用 Iconify 的?
自建 Iconify API 服务仅对「通过网络请求引入图标」的场景有效;如果你的项目是通过「npm 包方式引入 Iconify 图标」,本身就不需要额外部署服务,也无需自建 API。
npm 包方式引入(无需自建 API)
这种方式是将图标集以 npm 包的形式安装到项目中,比如我之前写的《iconfy 配置自动引入 Vue3+Vite3》一文,就是通过自动导入 @iconify-json/<图标集名称> 包来实现图标使用的。
优点: 实现了按需引入,即不是一次性引入所有的图标
缺点:
每次添加新的图标集项目的 package.json 文件中都会新增对应的 @iconify-json/* 依赖,但一个图标集可能只引入了一个图标。
且个人开发过程中,图标样式往往没法一步确定,经常需要反复替换、调整。这种情况下,先前安装的图标集 npm 包就会变成冗余依赖。诚然这其实不影响项目打包 ------Vite 或 Webpack 这类构建工具,本身会自动分析依赖树,只打包项目中实际用到的图标代码,不会因为安装了多余的图标集包就显著增加打包体积。
(不过本人有点 "代码洁癖",看着 package.json 里躺着一堆用不上的 @iconify-json/* 依赖就觉得不舒服,这纯属是无伤大雅的个人习惯~)
这种方式的特征也是缺点每次添加新的图标集,项目的 package.json 文件中都会新增对应的 @iconify-json/* 依赖。
网络请求方式引入(适合自建 API)
如果不想通过 npm 安装图标包,也可以使用 Iconify 官方提供的对应语言组件,通过网络请求动态加载图标。这里以 Vue 项目为例,基础使用代码如下:
vue
<template>
<!-- 使用图标,name 格式为「图标集前缀:图标名称」,比如 mdi:home -->
<Icon :icon="name" />
</template>
<script setup>
// 导入 Iconify 的 Vue 组件
import { Icon } from '@iconify/vue'
const props = defineProps({
name: { // 定义要使用的图标名称
type: String,
required: true,
},
})
</script>
Iconify 的核心构成
Iconify 并非单一工具,而是由多个核心开源库协同构成的生态。先理清这些库的关系,才能更好理解自建 API 服务的本质:
图标源:icon-sets
仓库地址:https://github.com/iconify/icon-sets
这是 Iconify 所有图标的数据源,它以 JSON 格式汇集了 200+ 开源图标集的内容,并且会定时自动更新。这些图标并非直接照搬原图标库,而是经过了专门处理:
- 通过 Iconify Tools(Iconify 官方的图标处理工具库)筛选和优化,剔除了脚本、事件监听器、字体、光栅图像等冗余内容。
- 图标中的单色填充色被替换为
currentColor,你可以通过修改文本颜色直接更改图标颜色,适配性更强。 - 图标代码被压缩优化,最大限度减小文件体积。
前端展示层:iconify
仓库地址:https://github.com/iconify/iconify
这是 Iconify 的前端核心框架,提供了各类前端框架(Vue/React/Angular 等)的图标组件,也就是在项目中用来展示图标的界面层。
后端服务层:Iconify API
仓库地址:https://github.com/iconify/api
这是为前端提供图标数据的后台服务,也是本次自建部署的核心对象 。前端组件会通过请求 API 来获取 icon-sets 中的图标数据。
构建 Iconfy API 服务
官方原本在 Docker Hub 提供了 Iconify API 的镜像(地址:iconify/api),理论上可以直接拉取镜像部署。但实际测试发现,国内 Docker Hub 镜像源并没有这个docker,国内无法直接拉取这个官方镜像 ,会出现超时错误(timeout error)
因此,笔者选择通过 Iconify API 的 GitHub 仓库代码自行打包构建 Docker 镜像 ,再进行容器化部署,接下将会介绍本次部署遇到的各种问题,即对应的解决方法,笔者采用的系统是Ubuntu 24.04.3 LTS。
前序准备
首先需要去 GitHub 克隆Iconify API代码,然后将代码压缩包上传到云端,在云端压缩,解压完成进入对应的目录,需要关注的文件是docker.sh和Dockerfile
bash
sftp username@server_ip # 建立sftp连接
put api-main.zip api-main.zip # 上传文件 put [上传文件路径][远端路径]
apt install -y unzip # 下载解压工具
unzip api-main.zip # 解压到当前目录
cd ./api-main
修改对应文件
由于官方给的 Dockerfile 是适配大公司的 CI 环境 / 公司内网镜像,因此构建镜像过程出现了很多问题,下面是需要修改的东西:
dockerfile
# 注释掉这三行
RUN cp /etc/apt/sources.list /etc/apt/sources.list.original
RUN ([ -s /tmp/sources.list.tmp ] && mv -f /tmp/sources.list.tmp /etc/apt/sources.list && cat /etc/apt/sources.list) || (cat /etc/apt/sources.list)
COPY tmp/build-ca-cert.crt /usr/local/share/ca-certificates/build-ca-cert.crt
# 删除
# Restore the original sources.list
([ -s /etc/apt/sources.list.original ] && mv /etc/apt/sources.list.original /etc/apt/sources.list) && \
# Remove the temporary build CA cert
rm -f /usr/local/share/ca-certificates/build-ca-cert.crt
如果你不想修改,可以通过ls -l 去判断自己系统内是否具有/etc/apt/sources.list & tmp/build-ca-cert.crt 这两个文件,即使存在,也需要判断大小是否为 0,为 0 也不行。
dockerfile
# 加速 apt
# 新增 阿里云 Debian Bullseye 源
RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list && \
sed -i 's|security.debian.org|mirrors.aliyun.com/debian-security|g' /etc/apt/sources.list
# 修改 RUN set -ex 行命令
# rm -rf /tmp/* && \ 替换为
rm -rf /var/lib/apt/lists/*
构建镜像
在修改对应文件后,在当前目录下执行:sh ./docker.sh 这里也可以不加 sh 如果没有报 -bash: ./docker.sh: Permission denied错误的话。
bash
# 检查镜像
docker image ls
# 出现下面两个镜像即构建成功
iconify/api 3.2.0
iconify/api latest
部署镜像
笔者是通过docker compose进行部署的,如果不想采用这个方案,可以将我下面提供的docker-compose.yml进行转化为对应的docker run命令
yaml
# docker-compose.yml
version: '3.9'
services:
iconify-api:
image: iconify/api:latest
container_name: iconify-api
restart: always # 容器故障自动重启
ports:
- "3000:3000" # 映射端口(宿主机:容器内)
environment:
# - ICONIFY_SOURCE=none # 禁用官方图标源
- HOST=0.0.0.0
- PORT=3000
# 性能优化:禁用不需要的功能(根据需求调整)
# - ENABLE_ICON_LISTS=false # 仅提供图标数据,不提供列表接口
# - ENABLE_SEARCH_ENGINE=false # 禁用搜索功能(节省内存)
- NODE_ENV=production # 生产环境标识
volumes:
# 挂载自定义图标集目录(本地图标集放在宿主机 ./icons 目录)
- /home/iconify/custom-icons:/data/iconify-api/icons:ro # ro=只读,防止容器内误修改
# 挂载缓存目录(持久化图标缓存,避免重启后重新加载)
- /home/iconify/iconify-cache:/data/iconify-api/cache
logging:
driver: "json-file"
options:
max-size: "5m" # 单个日志文件最大10MB
max-file: "3" # 最多保留3个日志文件
bash
# 没有docker compose 可以通过下面命令下载
apt install -y docker-compose-plugin # 验证 docker compose version
# 部署命令
docker compose up -d # 出现 Container iconify-api Started 即部署成功
# 验证
docker ps # 查看对应的docker有没有运行
docker logs iconify-api # 查看运行日志是否报错
# 记得开启ufw端口
# 外部请求
curl http://<ip>:3000/material-symbols.json?icons=database-search-outline
前端切换请求源
还是以 Vue为例子,只需要在main.ts(Vue 挂载的入口文件)中添加如下代码:
js
// 采用自定义iconfy API 服务器地址
import { addAPIProvider } from '@iconify/vue'
addAPIProvider('', {
resources: ['http:xxx:3000' || 'https://api.iconify.design'],
})
''指代默认请求地址,除此外还可以指定con集请求源地址,详细内容请看文档https://iconify.design/docs/api/providers.html
扩展源码讲解
这个部分,笔者将简要讲解一下 iconify-api代码的扩展使用。
api/icons/目录:存放自定义图标集(IconifyJSON 格式,可参考 icon-set 仓库内的 json 目录下的 json 文件)。- src/config/icon-sets.ts:配置图标集来源(决定是否加载自定义图标)。
图标集加载流程: src/config/icon-sets.ts 的 getImporters 函数定义图标来源,优先加载 icons/ 目录的自定义图标。若 ICONIFY_SOURCE=none,则仅加载 icons/ 目录下的图标集。
看过代码发现,其实在 API 的代码里并没有直接存放图标集 ,而是在服务启动时或更新时从指定源下载并本地存储 (缓存数据默认存储在容器内/data/iconify-api/cache下面),之后所有请求都从本地服务获取,无需依赖外部链接。(详见src/config/importers/该目录下对应不同ICONIFY_SOURCE配置有不同的加载方式,默认是full)