Icon Font在现代前端项目中的实战应用与优化技巧

玉浩酱~ 优化 阅读 1,883
赞 10 收藏
二维码
手机扫码查看
反馈

项目初期的技术选型

这个项目是个中后台系统,页面里图标特别多,从导航菜单到操作按钮,再到状态标记,少说也得上百个。一开始我寻思着用 SVG 雪碧图吧,毕竟现在主流都推 SVG,性能好、可缩放、还能做动画。但问题来了——团队里有几个新人,SVG 的维护成本对他们来说有点高,改个颜色都要手动改 fill 属性,而且项目工期紧,没时间搞复杂的构建流程。

Icon Font在现代前端项目中的实战应用与优化技巧

后来我翻了下老项目,发现之前有个用 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),在字体加载期间文字不可见。对于图标来说,就是“图标消失术”。

我试了几种方案:

  1. 把字体文件下载下来,放到本地 public 目录,避免跨域和 CDN 延迟
  2. 用 preload 预加载字体
  3. 加 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 替代。但在这之前,它还能再撑一阵子。如果你有更好的实践方式,欢迎留言交流。我也想看看别人是怎么平滑迁移的。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论