Next.js中字体加载闪烁问题怎么解决?

Zz文瑞 阅读 23

我在Next.js项目里用Tailwind CSS的Inter字体,但页面加载时总会出现字体闪烁。尝试过在_document.js里用标签引入fonts.googleapis.com,也加了font-display: swap,但问题还是存在。

代码是这样的:


// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html>
      <Head>
        <link rel="preconnect" href="https://fonts.gstatic.com" />
        <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet" />
      </Head>
      <Main />
      <NextScript />
    </Html>
  )
}

控制台没报错,但页面加载时字体还是会先显示系统默认字体再切换。有没有更好的优化方法?是不是应该用next/font的方式处理?

我来解答 赞 5 收藏
二维码
手机扫码查看
1 条解答
上官蓝月
字体闪烁问题在Next.js里太常见了,尤其是用Google Fonts这种外部字体的时候。你已经在_head.js_里加了_preconnect_和_font-display:swap_,这说明你已经懂一些优化手段了,但还不够彻底。根本原因在于:网络请求延迟 + 渲染阻塞 + 未预加载字体文件。

你现在的方式是通过动态加载CSS,然后CSS再去拉字体文件。这个过程至少有两轮网络请求(先下CSS,再下woff/woff2),页面渲染时浏览器只能先用系统默认字体占位,等字体下载完再换,这就导致了FOIT(Flash of Invisible Text)或FOUT(Flash of Unstyled Text)——也就是你说的“闪烁”。

真正靠谱的解法只有一个:换成 next/font。这不是建议,这是官方推的方案,而且能从根源上解决这个问题。

下面是具体怎么做:

第一步,把 _pages/_document.js_ 里的Google Fonts外链干掉,一个不留

import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
return (







)
}


注意,这里不需要再写任何link标签了。Head留空就行,后面交给next/font处理。

第二步,安装@next/font(如果你还没装的话)

npm install @next/font

第三步,在_app.js或者你需要用字体的地方引入Inter

比如在 pages/_app.js 里:

import { Inter } from '@next/font/google'
import '../styles/globals.css'

// 创建字体实例
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter', // 这样可以用于CSS变量
display: 'swap', // 关键!字体加载失败也不会阻塞渲染
})

export default function App({ Component, pageProps }) {
return (



)
}


解释一下关键点:

- @next/font/google 会自动预加载(preload)对应的woff2字体文件
- 它不是靠发请求去Google服务器拿CSS,而是在构建时就分析出要用哪些字重(400,500,700),然后生成base64内联或者静态资源链接
- 自动添加 rel="preload" 到关键字体资源,让浏览器提前下载
- 支持 display: 'swap',确保文本始终可见,不会出现空白期
- 更重要的是,它能把字体文件走你的CDN,而不是依赖gstatic.com,减少第三方依赖风险

第四步,更新Tailwind配置(可选但推荐)

虽然Inter现在挂到了全局CSS变量上,但为了统一管理,最好也在tailwind.config.js里声明一下:

module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['var(--font-inter)', 'ui-sans-serif', 'system-ui'],
},
},
},
plugins: [],
}


这样你在用 font-sans 的时候,实际就会用到你配置的Inter字体,而且带变量控制。

为什么这样就能解决闪烁?

因为 next/font 做了几件事:
1. 构建时确定字体需求,避免运行时才知道要加载哪个文件
2. 自动生成 preload link 标签插入到,优先级高,提前触发下载
3. 使用 font-face + local fallback 策略,尽可能利用缓存
4. 强制启用 font-display: swap,保证内容不卡住
5. 字体资源托管在自己的域名下,不受跨域和GFW影响(国内访问Google Fonts经常慢)

补充一点:如果你想进一步优化,还可以配合 Content-Security-Policy 或者 preconnect 给字体CDN提速,但在大多数场景下,用了next/font之后,首屏字体加载基本就稳了。

最后提醒一句:别再手写link引入Google Fonts了,尤其在生产环境。那玩意儿看着简单,实则坑多,性能不可控,还容易被墙。next/font才是正道。

你现在重新跑一遍项目,应该就不会看到字体闪了。要是还有,检查是不是其他地方还在引用别的字体,或者浏览器强缓存没清干净。
点赞 3
2026-02-11 09:03