引言
在现代 Web 应用中,字体不仅是视觉设计的核心元素,更是影响性能、用户体验与隐私合规 的关键因素。传统方式通过 <link>
标签从 Google Fonts 等 CDN 加载字体,虽然简单,但会带来:
- 额外网络请求(影响 LCP)
- 布局偏移(Layout Shift)(损害 CLS)
- 第三方追踪风险(违反 GDPR/CCPA)
为解决这些问题,Next.js 从 v13 开始内置 next/font
模块 ,提供自动自托管(Self-hosting) 能力,彻底消除外部依赖,实现"零布局偏移 + 隐私优先 + 极致性能"的字体加载体验。
一、next/font
的核心优势
传统方式 | next/font 方案 |
---|---|
从 fonts.googleapis.com 加载 |
字体文件打包进静态资源,由你的域名提供 |
首屏可能闪烁(FOUT/FOIT) | 自动注入 font-display: optional + 预加载,无布局偏移 |
第三方 Cookie/指纹风险 | 完全无外部请求,符合隐私法规 |
手动管理子集、权重、格式 | 自动优化:仅加载所需子集、优先 WOFF2、支持 Variable Fonts |
✅ 关键指标提升:
- CLS(Cumulative Layout Shift)趋近于 0
- LCP(Largest Contentful Paint)减少 100~300ms
- 第三方请求减少 1~2 个
二、使用 Google Fonts(推荐方式)
1. 基础用法:全局应用字体
在 app/layout.tsx
中导入并应用:
tsx
// app/layout.tsx
import { Geist } from 'next/font/google'
const geist = Geist({
subsets: ['latin'], // 必填:指定字符子集
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
💡 注意:
Geist
是 Vercel 官方推出的开源字体,性能优异,推荐优先使用subsets
必须显式声明,避免加载全量字体
2. 指定字重(非 Variable Font)
若使用非可变字体(如 Roboto),需手动指定 weight
:
tsx
import { Roboto } from 'next/font/google'
const roboto = Roboto({
weight: '400', // 或 ['400', '700'] 支持多字重
subsets: ['latin'],
})
3. 使用可变字体(Variable Fonts)------最佳实践
可变字体将多个字重/字宽合并为单个文件,体积更小、灵活性更强:
tsx
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
// 可变字体无需指定 weight,自动支持 100~900
})
✅ 推荐字体:Inter、Geist、Manrope、IBM Plex Sans(均支持 Variable)
三、使用本地字体(自定义字体)
适用于企业品牌字体(如 PingFang、HarmonyOS Sans)或私有字体。
1. 单字体文件
将 .woff2
文件放入 public/fonts/
目录:
tsx
// app/layout.tsx
import localFont from 'next/font/local'
const myFont = localFont({
src: './fonts/my-brand.woff2', // 相对于 public/
})
export default function RootLayout({ children }) {
return (
<html lang="zh-CN" className={myFont.className}>
<body>{children}</body>
</html>
)
}
2. 多文件组合(常规 + 粗体 + 斜体)
tsx
const roboto = localFont({
src: [
{
path: './fonts/Roboto-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './fonts/Roboto-Bold.woff2',
weight: '700',
style: 'normal',
},
{
path: './fonts/Roboto-Italic.woff2',
weight: '400',
style: 'italic',
},
],
})
📌 建议:
- 优先使用
.woff2
格式(压缩率最高)- 避免加载全量中文字体(体积过大),可考虑按需子集化(如使用
fonttools
)
四、高级技巧与注意事项
1. 组件级字体作用域
next/font
返回的 className
是局部作用域的,不会污染全局:
tsx
// components/Heading.tsx
import { Geist } from 'next/font/google'
const geist = Geist({ subsets: ['latin'] })
export function Heading() {
return <h1 className={geist.className}>Scoped Font</h1>
}
2. 避免重复加载
同一字体在多个组件中导入,Next.js 会自动去重,仅打包一次。
3. 中文字体优化建议
- 不要直接自托管完整中文字体(如思源黑体 > 10MB)
- 解决方案:
- 使用 Google Fonts 的 Noto Sans SC(支持子集)
- 或使用 font-spider 按页面内容生成子集
- 或采用"英文用
next/font
+ 中文 fallback 系统字体"策略:
css
font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', sans-serif;
五、性能验证
部署后,可通过以下方式验证效果:
- Network 面板 :无
fonts.googleapis.com
请求 - Coverage 面板:字体文件已内联或作为静态资源加载
- Lighthouse 报告 :
- CLS 应 < 0.1
- "Properly size images" 和 "Efficiently encode images" 不再报字体相关警告
总结
next/font
是 Next.js 对现代 Web 字体加载问题的终极解决方案。它通过:
- ✅ 自动自托管
- ✅ 智能子集切割
- ✅ 可变字体优先
- ✅ 零布局偏移设计
帮助开发者在不牺牲设计的前提下,大幅提升性能与隐私合规性。
行动建议 :
立即在你的 Next.js 项目中替换所有
<link href="https://fonts.googleapis.com/...">
为next/font
,享受开箱即用的高性能字体体验。