20MB 的字体文件太大了,我们把 Icon Font 压成了 10KB

在一次前端性能优化项目中,我们发现仅仅一个 icon font 文件就高达 20MB 。这不仅拖慢了首屏加载速度,还极大地浪费了带宽。最终,我们将它压缩到了 10KB,而不影响任何功能表现。

这一过程背后,涉及的不仅是压缩,而是对「构建流程」「字体格式」「加载策略」「字形定制」的全盘重构。本文将逐步拆解这场"减重手术",帮助你理解 icon font 是如何成为性能黑洞的,又是如何优雅瘦身的。


问题:20MB 的 icon font 是怎么来的?

大字体文件往往是由于以下原因造成的:

  • 过度收录:设计同学导出了一整套 2000 多个图标的 icon font,实际只用了其中几十个。
  • 全量打包:工具如 Icomoon、Fontello、FontForge 默认导出全量字形。
  • 格式冗余 :一个字体文件常包含 .ttf, .woff, .woff2, .eot, .svg 多种格式,全打包增加体积。
  • 不做 Subset(子集提取):没有剔除未使用的字形。

最终结果就是:用户下载了 2000 个图标,只为了看到那 20 个常用 icon。


目标:精简为只包含实际使用 icon 的最小字体

如果你只用了 <i class="icon-chevron-down"></i><i class="icon-close"></i><i class="icon-search"></i> 三个图标,那字体文件里应该只包含这三个图形。

核心理念是:用子集字体(Subset Font)只保留被真正使用的字形。


解决方案路线图

✅ 步骤一:收集实际用到的 icon

  • 全站代码扫描,提取 icon class(或 unicode)
  • 工具:自定义脚本、AST 分析、静态资源分析工具
bash 复制代码
# 示例:查找 iconfont 使用的 class 名称
grep -roh 'icon-[a-zA-Z0-9_-]\+' ./src | sort | uniq > used-icons.txt

✅ 步骤二:精简 icon 到最小集合

工具选择:

  • IcoMoon App:可视化管理图标,导出精简 icon font
  • FontSubset:支持上传字体,自动子集提取
  • pyftsubset(来自 fonttools):命令行方式自动提取子集

例:使用 pyftsubset

bash 复制代码
pyftsubset original.ttf \
  --unicodes=U+E001,U+E002,U+E003 \
  --output-file=subset.ttf \
  --flavor=woff2 \
  --layout-features='*' \
  --no-hinting \
  --glyph-names

说明:

  • --unicodes 指定只保留的字符
  • --flavor=woff2 输出现代浏览器首选格式
  • --no-hinting 去除微调信息,减小文件体积

✅ 步骤三:只保留必要的字体格式

浏览器现代化后,建议:

  • 只保留 .woff2(现代浏览器支持)
  • 视兼容性决定是否保留 .woff(老一点的 Chrome/Firefox)
  • 移除 .eot / .svg / .ttf 除非需要极限兼容 IE6+

字体大小差异:

格式 同内容文件大小
TTF 40KB
WOFF 28KB
WOFF2 10KB

✅ 步骤四:字体精简之后如何正确加载?

CSS 示例:

css 复制代码
@font-face {
  font-family: 'MyIcons';
  src: url('icons.woff2') format('woff2');
  font-display: swap;
}

重点字段说明

  • font-display: swap:加速首次渲染
  • format('woff2'):浏览器可判断是否支持

✅ 步骤五:如果你用的是组件库的内建 iconfont

Ant Design、Element UI、Bootstrap Icons 等往往内置大量 iconfont。优化策略如下:

  • 替换为 SVG 图标组件 (例如 Iconify

  • 只引入需要的图标模块

    • Antd 4.x 以上支持按需引入图标(非字体形式)
  • 使用 Tree-shaking 友好的 SVG icon 方案

    • @iconify/react@icon-park/react

成果验证

经过上述处理:

  • 初始字体大小:20.3MB
  • 实际保留字形数量:12 个
  • 精简后字体(.woff2)大小:10.4KB
  • 首屏加载 TTI 提升:约 800ms
  • Lighthouse 性能评分:+9 分

额外干货:你可能不知道的字体优化技巧

🧠 1. 使用 base64 inline 的 icon font 并非总是好事

虽然可减少 HTTP 请求,但:

  • 无法缓存(每次 HTML 载入)
  • 增加 HTML 大小
  • 不利于 CDN 优化和延迟加载

通常只有在 icon font < 5KB 且需要打包进组件时,才考虑 base64。


🧠 2. 字体子集可以配合 SSR 实现动态优化

在 SSR 应用(如 Next.js)中,可以:

  • 在构建阶段根据页面中实际 icon 自动生成对应的字体子集
  • 动态注入只需要的 icon font,达到更极致的优化效果

🧠 3. 替代方案:彻底摆脱 icon font,用 SVG

SVG 优点:

  • 完全控制颜色/动画
  • 无需额外字体解析
  • 体积更小,支持按需加载
  • 更适合现代组件式开发(React/Vue)

推荐库:

  • Iconify(80+ 图标集统一封装)
  • unplugin-icons(Vite 项目自动加载)
  • Heroicons、Feather、Lucide、Tabler 等

你还在用几兆的 icon font,不妨静下心来,用一下午把它瘦成精悍的 10KB, 别让一堆你永远不会用到的图标,霸占用户的加载时间。

📌 你可以继续看我的系列文章

相关推荐
乐予吕2 分钟前
Promise 深度解析:从原理到实战
前端·javascript·promise
P7Dreamer3 分钟前
优雅封装:Vue3 + Element Plus 智能紧凑型搜索组件开发实践
前端·javascript
Turing_0104 分钟前
HarmonyOS隐私保护全攻略:从入门到精通
前端
Turing_0104 分钟前
HarmonyOS应用安全全攻略:从系统到代码的全面防护
前端
Beginner x_u10 分钟前
[AJAX 实战] 图书管理系统下 编辑图书
前端·javascript·ajax·bootstrap
Ace_317508877611 分钟前
# 唯品会商品详情接口开发指南
前端
Beginner x_u13 分钟前
【AJAX 实战】图书管理系统上 渲染图书列表+新增图书+删除图书
前端·ajax·bootstrap
恋猫de小郭17 分钟前
Flutter 里的像素对齐问题,深入理解为什么界面有时候会出现诡异的细线?
android·前端·flutter
anyup30 分钟前
AI 也救不了的前端坑,你遇到过吗?社区、AI、源码三重排查!
前端·数据可视化·cursor
tager37 分钟前
还在为跨框架的微信表情包烦恼?我写了个通用的,拿去吧!🚀
前端·vue.js·react.js