You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js中缓存字体?解决FOUT及重复下载问题

解决FOUT问题并优化字体缓存方案

咱们先拆解下你当前的核心诉求:已经搞定了FOIT(不可见文本闪烁),但FOUT(可见文本闪烁)依然存在,同时希望用户再次访问时不用重复下载字体。结合你给出的代码,下面是针对性的优化方案:


一、彻底消除FOUT的核心调整

FOUT的本质是浏览器先渲染fallback字体,等自定义字体加载完成后切换导致的视觉闪烁。要解决这个问题,得结合font-display策略、FontFaceObserver的逻辑优化,同时尽可能缩小fallback字体和自定义字体的排版差异:

1. 调整CSS字体策略与排版匹配

你的现有CSS可以做两处关键修改:一是换用更严格的font-display值避免延迟切换,二是让fallback字体的行高、字重贴近自定义字体,减少切换时的布局偏移:

/* 基础fallback样式,尽可能贴近自定义字体的排版特征 */
html {
  font-family: sans-serif;
  -webkit-font-smoothing: antialiased;
  /* 添加和自定义字体一致的行高基准,减少切换时的跳动 */
  line-height: 1.5;
}

/* 字体加载完成后替换样式,移除冗余的html选择器 */
.fonts-loaded {
  font-family: 'Avenir', sans-serif;
}

@font-face {
  font-family: 'Avenir';
  src: url('/static/fonts/Avenir.ttc');
  /* optional比swap更严格:如果字体没在关键渲染周期内加载完成,就一直用fallback */
  font-display: optional;
  font-style: normal;
}

@font-face {
  font-family: 'Poppins';
  src: url('/static/fonts/Poppins-Regular.ttf');
  font-weight: 400;
  font-style: normal;
  font-display: optional;
}

2. 优化FontFaceObserver的逻辑

你当前的代码里,Avenir加载完成后没有更新类名,而且两个字体的加载逻辑可以合并,避免单个字体加载完成就切换导致的局部闪烁:

componentWillMount() {
  if (process.browser) {
    const html = document.documentElement;
    html.classList.add('fonts-loading');

    // 同时监听两个字体的加载状态,全部完成后再切换样式
    Promise.all([
      new FontFaceObserver('Poppins').load(null, 5000),
      new FontFaceObserver('Avenir').load(null, 5000)
    ]).then(() => {
      console.log('Poppins和Avenir字体都加载完成了');
      html.classList.remove('fonts-loading');
      html.classList.add('fonts-loaded');
    }).catch(() => {
      console.log('至少一款字体加载失败');
      html.classList.remove('fonts-loading');
      html.classList.add('fonts-failed');
    });
  }
}

二、实现字体持久缓存,避免重复下载

要让浏览器缓存自定义字体,核心是配置正确的服务器缓存响应头,同时保证字体文件的URL稳定:

1. 配置静态字体的缓存规则

如果是Next.js项目,可以在next.config.js中给字体文件设置长期缓存:

module.exports = {
  async headers() {
    return [
      {
        source: '/static/fonts/(.*)',
        headers: [
          {
            key: 'Cache-Control',
            // 缓存一年,且标记文件不可变,浏览器不会再发验证请求
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
    ];
  },
};

如果是其他服务器,也要确保给字体文件设置类似的Cache-Control响应头,让浏览器知道可以长期缓存这些文件。

2. 避免重复加载字体

你当前同时用了本地Poppins和Google Fonts的Poppins,这会导致重复下载,建议二选一:如果用本地字体,就移除Google Fonts的预加载链接;如果需要Google Fonts的多字重版本,要确保@font-face声明覆盖对应的字重,避免冲突。


三、额外的细节优化

  • 字体子集化:如果只用到字体的部分字符(比如仅英文),可以生成子集化字体文件,减小体积,加快加载速度,从根源降低FOUT的概率。
  • 预加载本地字体:给本地字体添加preload标签,提前触发加载:
<link rel="preload" href="/static/fonts/Avenir.ttc" as="font" type="font/ttc" crossorigin>
<link rel="preload" href="/static/fonts/Poppins-Regular.ttf" as="font" type="font/ttf" crossorigin>

注意必须加crossorigin属性,即使是同域资源,字体预加载也需要这个属性才能正常缓存。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:36:10