字体子集化不是做完就完了。去年 9 月我们给一个 SaaS 官网做的子集,三个月后新上的产品文案里出现 17 个缺字,前端只能临时回退全量包,首屏字体从 280KB 涨回 2.3MB,之前的速度优化等于白做。
子集为什么会失效
子集是按"生成那一刻页面上的字"切的。市场部改一版文案、加一个客户名、发一篇带生僻词的博客,字符集就不够了。浏览器遇到缺字会用回退字体渲染,视觉上跳出一个字形不一样的汉字,比豆腐块好一点,但一样难看。
这个站先是客服收到反馈,说标题里有个字特别细。前端查了两天才定位到字体子集。临时方案是回退全量包,代价是所有人都变慢。
把字符集变成构建产物
我们的做法是把字符集从"一次性脚本"改成"每次构建自动生成":
- 构建时从 CMS 拉全站文案和博客正文,统计去重字符
- 去掉 ASCII 和标点,剩下的中文字符交给自己写的子集脚本
- 在统计结果上留 5% 冗余,按常用字表补一批
- 产物随构建走,版本号和内容一起变
好处是文案更新会触发子集更新,不会悄悄缺字。
缺字监控
构建只能覆盖 CMS 里的内容,用户输入、第三方评论、搜索关键词都覆盖不到。我们加了一条前端监控:用 canvas 测量几个生僻字在自定义字体下的宽度,和回退字体对比,不一样就上报。
这个方案不完美,但能抓到大部分问题。上线两个月抓到 4 次缺字,都是编辑器里直接粘进来的内容。
一些具体数字
这个站子集后首屏字体 280KB,覆盖 4200 个字符,font-display 用 swap。首屏文字在 1.1 秒内用回退字体渲染,1.4 秒切换成自定义字体,CLS 控制在 0.02 以内。
回退字体用系统黑体,字宽差异小,切换不明显。如果换成宋体这种字形差别大的,就得配合 size-adjust 微调,不然整段重排。
我的建议
内容更新频繁的站,别做纯静态子集,做构建期自动生成。内容几乎不变的站,比如产品官网,手工子集加每季度复查也行,但要把复查写进流程,别靠记忆。
还有个更省事的方案:正文直接用系统字体栈,只在标题上用自定义字体。中文系统字体在 Windows 和 macOS 上都不难看,这一刀下去能省掉大部分字体问题。
元析智能
为软件公司做 SEO 和 GEO 优化