Icon Font在现代前端项目中的实战应用与优化技巧
项目初期的技术选型
这个项目是个中后台系统,页面里图标特别多,从导航菜单到操作按钮,再到状态标记,少说也得上百个。一开始我寻思着用 SVG 雪碧图吧,毕竟现在主流都推 SVG,性能好、可缩放、还能做动画。但问题来了——团队里有几个新人,SVG 的维护成本对他们来说有点高,改个颜色都要手动改 fill 属性,而且项目工期紧,没时间搞复杂的构建流程。
后来我翻了下老项目,发现之前有个用 Icon Font 的方案还挺稳的。虽然现在大家都说 Icon Font 过时了,但说实话,在这种图标量大、更新频繁、又需要兼容老浏览器的场景下,它真不是最差的选择。关键是:简单、易上手、一行 CSS 就能全局控制颜色和大小。于是我就拍板了:这次就用 Icon Font,先上线再说。
怎么搭起来的
流程其实不复杂。我们用的是阿里那个 iconfont.cn 平台,设计师把图标上传进去,我生成一个 Webfont 链接,然后引入项目。最后通过类名调用图标,跟用字体一样。
具体操作:
- 在 iconfont.cn 创建项目,上传 SVG 图标
- 生成在线链接,比如:
https://at.alicdn.com/t/font_xxxxxx.css - 在项目的
index.html里加上 link 引入
<link rel="stylesheet" href="https://at.alicdn.com/t/font_xxxxxx.css">
然后就可以在代码里用了:
<i class="iconfont icon-home"></i>
<i class="iconfont icon-setting"></i>
CSS 部分基本不用动,除非你想微调间距或者垂直对齐:
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
vertical-align: -1px; /* 调整图标和文字的对齐 */
}
到这里,看似一切顺利,本地跑起来也没啥问题。但一上测试环境,就开始出幺蛾子了。
最大的坑:字体加载失败 + 页面闪一下
测试同学反馈说,首页刚打开的时候,图标区域是一片空白,等个 1~2 秒才突然蹦出来,体验非常差。我一开始以为是网络慢,但自己用弱网模拟也复现不了这么严重的延迟。后来抓包一看,发现字体文件(woff2)是从 CDN 加载的,首屏渲染时根本没等到字体回来,浏览器就先用 fallback 字体画了内容——结果就是图标显示成方块或乱码,等字体加载完才重绘。
这叫 FOIT(Flash of Invisible Text),在字体加载期间文字不可见。对于图标来说,就是“图标消失术”。
我试了几种方案:
- 把字体文件下载下来,放到本地 public 目录,避免跨域和 CDN 延迟
- 用 preload 预加载字体
- 加 loading 状态,等字体 ready 再渲染图标区域
前两个有用,但第三个太重了,会影响首屏速度。最后我选择了本地部署 + preload 的组合拳。
<link rel="preload" href="/fonts/iconfont.woff2" as="font" type="font/woff2" crossorigin>
同时在 webpack 里把字体文件复制到输出目录,确保路径正确。这一步折腾了半天,因为 woff2 文件默认不会被 webpack 处理,得手动配置 file-loader 或 asset modules。
还遇到个诡异问题:Safari 下字体死活不显示。查了半天才发现是缺少 ttf 格式支持。Webkit 内核有些版本对 woff2 支持不好,所以得保留 ttf 作为降级。
@font-face {
font-family: 'iconfont';
src: url('/fonts/iconfont.eot');
src: url('/fonts/iconfont.eot?#iefix') format('embedded-opentype'),
url('/fonts/iconfont.woff2') format('woff2'),
url('/fonts/iconfont.woff') format('woff'),
url('/fonts/iconfont.ttf') format('truetype');
font-display: swap; /* 关键!用 swap 让文本先显示 */
}
加上 font-display: swap 之后,总算解决了闪屏问题。即使字体没加载完,也会先用 fallback 显示占位,等回来再替换,用户体验顺滑多了。
图标命名冲突和维护混乱
另一个问题是团队协作。设计师不断加新图标,前端也经常删旧图标,结果出现过一次线上事故:某个按钮图标突然变成了笑脸,因为类名重复了。
原因是 iconfont.cn 默认会按上传顺序生成类名,比如你传了个“删除”,它可能叫 icon-shanchu,但别人传了个同音不同义的,也叫这个名字,就撞了。
后来我们强制规定:所有图标必须由我统一管理,上传后立刻在文档里登记类名和用途,禁止直接在平台修改。同时改用 symbol 方案作为长期替代计划。
不过到现在为止,还是偶尔会有同事不小心用了错误的类名,只能靠 code review 拦住。这点确实不如 SVG 组件那样有类型提示。
最终的解决方案
目前线上跑的是这套方案:
- 字体文件本地化部署,走项目静态资源
- 使用 preload + font-display: swap 提升加载体验
- 统一维护 iconfont 项目,定期导出最新 CSS
- 关键页面首屏图标,用 base64 内联一个小图标集(只包含首页用到的几个)
最后一条是临时优化。我把首页用到的三个核心图标抽出来,单独打成一个极小的 icon font,转成 base64 嵌入 CSS,确保首屏零延迟。
@font-face {
font-family: 'iconfont-inline';
src: url('data:font/woff2;base64,d09GMgABAAAAAA...') format('woff2');
font-display: swap;
}
虽然增大了 CSS 体积(大概多了 2KB),但换来了首屏图标秒出,值得。
回顾与反思
回头看,用 Icon Font 确实不是最优解,但它在特定场景下依然能打。尤其是当你面对一个多人协作、工期紧、还要兼容 IE 的项目时,它的低成本和高稳定性反而成了优势。
做得好的地方:
- 加载性能问题基本解决,FOIT 消失了
- 样式统一,改颜色一行 CSS 全局生效
- 对新手友好,不用懂 SVG 结构也能用
还能优化的地方:
- 没有 Tree Shaking,所有图标打包在一起,哪怕只用一个也要下完整字体
- 无法做动态颜色(比如渐变色图标),受限于字体本质
- 语义化差,
<i class="iconfont icon-xxx">对无障碍访问不友好
其实我们已经在写一个 SVG Symbol 的替代方案,准备下个版本切过去。但现在这套 Icon Font 还会继续维护一阵子,毕竟改不动了——不是技术上改不动,是排期上没空动。
结语
以上是我踩坑后的总结。这个方案不完美,甚至有点土,但在真实项目里,很多时候“能跑”比“先进”更重要。如果你也在搞类似的中后台项目,又不想一开始就上复杂架构,Icon Font 依然是个可以考虑的选项。
当然,长远来看它肯定会被 SVG 替代。但在这之前,它还能再撑一阵子。如果你有更好的实践方式,欢迎留言交流。我也想看看别人是怎么平滑迁移的。

暂无评论