独立站字体优化:系统字体、font-display、预加载与FOIT
一、字体为什么会阻塞文字显示
浏览器拿到字体文件之前不知道文字该长什么样,默认行为是让文字不可见(FOIT,Flash of Invisible Text)。这个隐藏期在旧浏览器是3秒,新浏览器改成1秒后强制用fallback字体显示。问题在于:首屏正文正是LCP元素,如果它在等字体,LCP就被字体下载时间拖住。
独立站典型错误:主题作者为了”设计感”挂了Google Fonts的Inter或Roboto全字重(100至900共9个字重),每个字重100KB,一次加载900KB。3G网络下光字体就下载3至5秒,用户看到的是一片空白。Core Web Vitals里字体相关指标见Core Web Vitals优化,CDN怎么配见CDN全球加速。
| 问题 | 对性能影响 | 典型体积 |
|---|---|---|
| 加载全字重(9档) | 首屏多下载800KB | 每字重80至120KB |
| 未子集化的中文字体 | 单文件2至8MB | 远超首屏预算 |
| font-display默认block | FOIT隐藏1至3秒 | 文字不可见 |
| 未preload关键字体 | 字体发现晚,延迟200ms | LCP被拖慢 |
| 用多家族(>3个) | 每个家族都要下载 | 累计300至500KB |
二、系统字体栈:B2B站的默认答案
系统字体栈(system font stack)用的是用户设备上已经装好的字体,零网络请求,立即显示。B2B独立站的内容以参数表、案例描述、技术文章为主,排版上不需要花体或品牌定制字体,系统字体栈在视觉上完全够用,而且跨平台一致。
一个可直接用的CSS栈:font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, “PingFang SC”, “Microsoft YaHei”, sans-serif。这个栈覆盖macOS、Windows、Android、iOS,中英文都有对应系统字体。反直觉判断:用系统字体不会让网站看起来”不专业”,反而是GitHub、Stripe、Linear这些设计标杆站都用系统字体栈。WP主题怎么选见WordPress技术栈。
| 方案 | 首屏文字 | 额外下载 | 适用站型 |
|---|---|---|---|
| 纯系统字体栈 | 立即显示 | 0KB | 大多数B2B独立站 |
| 1个Web字体标题 | fallback后替换 | 30至80KB | 强调品牌感的站 |
| 2个Web字体家族 | fallback后替换 | 80至150KB | 设计驱动的品牌站 |
| 3个以上Web字体 | FOUT/FOIT明显 | >200KB | 不建议 |
| 中文Web字体全量 | FOIT数秒 | 2至8MB | 几乎不可行 |
三、font-display:swap与FOIT:别让文字藏起来
font-display是@font-face里的属性,控制字体加载期间文字怎么显示。四个值:auto(浏览器默认,过去是block)、block(短暂隐藏3秒后fallback)、swap(立即用fallback显示,字体好了再替换)、optional(浏览器自己决定,倾向不下载)。
B2B独立站应该一律用swap。block在3G网络下等于首屏空白3秒,访客早走了;optional适合品牌非关键的字体,但不可控。一个常见误解是”swap会造成字体闪烁(FOUT)”,但对B2B站,先让用户读到参数和联系方式,比等一个花体标题重要得多。字体闪一下完全可以接受,文字看不到才是真损失。CSS/JS加载策略见CSS/JS优化。
| font-display | 阻塞期 | 字体加载完后 | 适用场景 |
|---|---|---|---|
| auto/block | 3秒内文字不可见 | 替换为Web字体 | 不建议 |
| swap | 立即用fallback显示 | 字体好了替换 | B2B站默认 |
| fallback | 100ms隐藏,然后fallback | 3秒内加载完才替换 | 半关键字体 |
| optional | 100ms隐藏,然后fallback | 本次访问不替换 | 纯装饰字体 |
四、preload与子集化:让该快的字体快起来
如果首屏标题必须用Web字体,用preload告诉浏览器提前高优先级下载:在head里加<link rel=”preload” href=”font.woff2″ as=”font” type=”font/woff2″ crossorigin>。这样字体在CSS解析之前就开始下载,LCP能提前200至400毫秒。注意crossorigin属性不能省,否则preload会失效。
子集化(subset)是另一个必须做的优化。英文字体只保留英文+数字+常用符号,体积从100KB压到30KB;中文字体因为字符集太大,即使子集化也有200KB至1MB,所以中文站才建议直接系统字体。用glyphhanger或Google Fonts的text参数只加载用到的字符。一个具体场景:首屏标题只用了20个汉字,就只subset这20个字,文件从4MB压到5KB。CDN缓存见CDN选型对比。
| 字体优化动作 | 体积/延迟收益 | 实施难度 | 建议 |
|---|---|---|---|
| 只用woff2格式 | 比ttf小30% | 低 | 必做 |
| 只加载400和700字重 | 减60%下载 | 低 | 必做 |
| 子集化英文字符集 | 再减50% | 中 | 推荐 |
| preload首屏字体 | LCP提前200至400ms | 低 | 首屏标题用 |
| 中文全量Web字体 | 负面 | 高 | 不要做 |
五、验收:用DevTools看字体加载瀑布
字体优化做完怎么验收?打开Chrome DevTools的Network面板,过滤Font,看三件事:字体文件数量(应该≤2个家族、每家族≤2个字重)、字体文件大小(单个≤50KB为健康)、字体开始下载的时间点(preload后应该在最早的请求里,不是CSS解析后才发现)。任何一个字体文件超过100KB,都值得回去查是不是字重或子集没配好。
再用Lighthouse看”避免在Web字体上使用布局移位”和”预加载关键请求”两个audit。一个可执行清单:font-display全部是swap、首屏字体preload、woff2格式、无ttf/eot老格式、Google Fonts用display=swap参数。多语言站字体怎么处理,见多语言站的图表处理。站点监控见站点监控告警。
| 验收项 | 合格标准 | 测量工具 |
|---|---|---|
| font-display | 全部swap或fallback | CSS审查 |
| 首屏字体preload | 首屏标题字体已preload | DevTools Network |
| 字体文件格式 | 只有woff2 | Network过滤Font |
| 单字体体积 | ≤50KB | Network Size列 |
| 字体家族数 | ≤2个 | CSS审查 |
| LCP 75分位 | ≤2.5秒 | CrUX/RUM |
FAQ 常见问题
B2B独立站一定要用Web字体吗?
font-display:swap和block差在哪?
中文字体能用Web Font吗?
preload字体为什么要加crossorigin?
Google Fonts怎么加载最快?
字体优化完怎么验收?
六、FOUT与布局移位:换字体时怎么不晃
用swap策略后,fallback字体替换成Web字体时,因为字宽字高不同,页面可能会跳动,这就是CLS(累积布局偏移)的来源。Stripe曾做过测量:一次字体替换平均造成0.05至0.1的CLS,刚好卡在合格线0.1的边上。B2B站因为正文长、段落多,替换造成的换行差异会让图片位置上下跳。
处理方法是font-size-adjust和size-adjust两个描述符。在@font-face里用size-adjust: 105%和ascent-override把Web字体的度量调整到和fallback接近,替换时几乎不跳。另一个更省事的办法是选fallback字体和Web字体度量接近的组合——比如Inter的fallback选Arial,或者直接用系统字体栈不换。一个具体验收:用Lighthouse看”避免在Web字体上使用布局移位”audit,如果分数低,先看是不是swap造成的,再决定要不要加size-adjust。
| FOUT处理方式 | CLS改善 | 实施难度 |
|---|---|---|
| 不处理,接受闪烁 | 0.05至0.1 | 无 |
| size-adjust微调度量 | 降到0.02以下 | 中,需测 |
| 选度量接近的fallback | 降到0.03以下 | 低 |
| 干脆不用Web字体 | 0 | 零成本 |
写在最后
字体优化的核心判断很简单:B2B站的用户来读参数、看案例、找联系方式,不是来欣赏排印艺术。系统字体栈在95%的场景下是正确答案,省下的几百KB和几百毫秒比视觉差异重要得多。即使保留Web字体,也严格控制在两个家族、两个字重、woff2格式、swap加载这四条线以内。
落地三步:第一,本周把主题字体改成系统字体栈,删掉多余的Google Fonts引用;第二,保留的Web字体全部加font-display:swap,只留400和700字重,自托管woff2文件;第三,首屏标题字体加preload并补crossorigin。两周后看LCP和字体相关audit有没有改善。
一句可以记住的话:用户不会因为你的网站用了Inter而多留一秒,但会因为等字体加载3秒而直接关掉。字体是细节,但细节决定了首屏那2.5秒能不能抓住人。
