字体
@nuxt/fonts 模块自动优化字体加载,避免布局偏移(FOIT/FOUT),提升 Core Web Vitals 评分。
为什么需要字体优化?
| 问题 | 未优化 | 优化后(@nuxt/fonts) |
|---|---|---|
| FOIT(不可见文本闪烁) | 自定义字体加载前文本不可见 | 先显示系统字体,自定义字体加载后替换 |
| CLS(布局偏移) | 字体切换时页面跳动 | 自动计算 size-adjust,零偏移 |
| 请求瀑布流 | CSS 解析 → 发现字体 → 下载 → 显示 | 预加载关键字体,减少等待 |
| 本地字体管理 | 手动配置 @font-face | 自动检测 assets/fonts/ 中的字体 |
| 重复下载 | 每次访问都下载 | 浏览器缓存 + 本地代理 |
字体加载如何影响性能?
- 浏览器解析 HTML → 发现 CSS 引用
- 下载 CSS → 解析发现
@font-face - 下载字体文件(可能数百 KB) → 文本渲染被阻塞
- 在字体加载完成前,浏览器要么隐藏文本(FOIT),要么用系统字体(FOUT)
@nuxt/fonts 通过预加载、本地代理、size-adjust 等技术优化这个过程。
安装
npm install @nuxt/fontsexport default defineNuxtConfig({
modules: ['@nuxt/fonts'],
})@nuxt/fonts 做了什么?
- 自动检测 CSS 中的
font-family引用 - 从 Google Fonts 等 Provider 下载字体文件
- 代理字体请求到本地(避免 DNS 查询和跨域延迟)
- 自动生成
@font-face声明 - 预加载首屏关键字体
- 自动计算
size-adjust减少布局偏移
使用 Google Fonts
export default defineNuxtConfig({
fonts: {
families: [
{ name: 'Inter', provider: 'google', weights: [400, 500, 700] },
{ name: 'Noto Sans SC', provider: 'google', weights: [400, 700] },
],
},
})body {
font-family: 'Inter', 'Noto Sans SC', sans-serif;
}@nuxt/fonts 会自动处理
- 从 Google Fonts 下载字体文件
- 代理到本地路径(如
/_fonts/inter-400.woff2) - 生成
@font-face声明并注入 CSS - 预加载首屏需要的字体
无需手动写 @font-face
@nuxt/fonts 全自动处理。
使用本地字体
将字体文件放在 app/assets/fonts/ 目录中,@nuxt/fonts 会自动检测:
app/assets/fonts/
├── custom-font.woff2
└── custom-font-bold.woff2body {
font-family: 'CustomFont', sans-serif;
}自动检测的原理
@nuxt/fonts 扫描 assets/fonts/ 目录,根据文件名推断字体名称和样式(如 custom-font-bold.woff2 → CustomFont、weight 700),自动生成 @font-face 声明。
手动配置本地字体
如果自动检测不满足需求:
export default defineNuxtConfig({
fonts: {
families: [
{
name: 'CustomFont',
src: [
{ path: '~/assets/fonts/custom-font.woff2', weight: 400, style: 'normal' },
{ path: '~/assets/fonts/custom-font-bold.woff2', weight: 700, style: 'normal' },
],
},
],
},
})传统方式——手动 @font-face
不使用 @nuxt/fonts 时,手动配置 @font-face:
/* app/assets/css/fonts.css */
@font-face {
font-family: 'CustomFont';
src: url('~/assets/fonts/custom-font.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'CustomFont';
src: url('~/assets/fonts/custom-font-bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}body {
font-family: 'CustomFont', sans-serif;
}INFO
️ 手动 @font-face 的缺点:需要手动管理预加载、size-adjust、缓存策略等 推荐使用 @nuxt/fonts 自动处理
字体 Provider
| Provider | 说明 | 适用场景 | 网络需求 |
|---|---|---|---|
google | Google Fonts | 最常用,免费字体丰富 | 需访问 google.com(国内可能不稳定) |
bunny | Bunny Fonts | GDPR 友好(不追踪用户) | 需访问 bunny.net |
fontsource | Fontsource | 通过 npm 包提供字体 | 仅需 npm,构建后零网络依赖 |
fontshare | Fontshare | 免费优质字体 | 需访问 fontshare.com |
adobe | Adobe Fonts | 订阅了 Adobe 创意云 | 需访问 Adobe 服务 |
local | 本地字体文件 | 自定义字体 | 无 |
Google Fonts vs Bunny Fonts vs Fontsource
- Google Fonts:字体最全,但在中国访问可能不稳定
- Bunny Fonts:GDPR 友好,不追踪用户,在中国访问更稳定
- Fontsource:通过 npm 安装字体包,构建时完全本地化,零外部网络请求,最适合网络受限环境
中国用户推荐
优先使用 Fontsource(零网络依赖)或本地字体,避免 Google Fonts 加载慢的问题。
配置不同 Provider
export default defineNuxtConfig({
fonts: {
families: [
{ name: 'Inter', provider: 'google' }, // Google Fonts
{ name: 'DM Sans', provider: 'bunny' }, // Bunny Fonts
{ name: 'Satoshi', provider: 'fontshare' }, // Fontshare
{ name: 'CustomFont', provider: 'local' }, // 本地字体
],
},
})使用 Fontsource(推荐网络受限环境)
Fontsource 通过 npm 包提供 Google Fonts,无需任何外部网络请求:
# 安装需要的字体包
npm install @fontsource/inter<!-- app/app.vue 或任意页面 -->
<script setup>
import '@fontsource/inter/400.css'
import '@fontsource/inter/700.css'
</script>body {
font-family: 'Inter', sans-serif;
}Fontsource 的优势
字体通过 npm install 下载到 node_modules,构建时完全本地处理。不需要访问 Google Fonts 或任何外部服务,非常适合国内开发环境或容器化部署。
搜索可用字体包:https://fontsource.org/fonts
禁用特定 Provider
如果某些 Provider 网络不可用(如 Google Fonts 在中国),可以禁用它们:
export default defineNuxtConfig({
fonts: {
providers: {
google: false, // 禁用 Google Fonts provider
googleicons: false, // 禁用 Google Icons provider
},
},
})INFO
️ @nuxt/fonts 可能作为其他模块的依赖自动安装(如 @nuxt/ui 依赖 @nuxt/fonts) 此时不能通过 npm uninstall 卸载,但可以通过禁用 provider 来避免网络请求。@nuxt/fonts 没有 enabled: false 选项,禁用 provider 是唯一的关闭方式
font-display——控制字体加载行为
font-display 决定了字体加载过程中文本如何显示:
| 值 | 阻塞期 | 交换期 | 适用场景 |
|---|---|---|---|
auto | 浏览器决定 | 浏览器决定 | 默认,不推荐 |
block | 最多 3 秒 | 无限 | 图标字体(必须等字体加载) |
swap | 0 秒 | 无限 | 推荐——文本始终可见 |
fallback | 100ms | 3 秒 | 对字体要求较高的场景 |
optional | 0 秒 | 0 秒 | 只在缓存可用时使用 |
font-display: swap(推荐)的行为
- 浏览器立即用系统字体渲染文本(文本始终可见)
- 自定义字体下载完成后,替换系统字体
- 不再有"不可见文本闪烁"(FOIT)
INFO
️ swap 的副作用:字体切换时如果字体的尺寸差异大 会出现"布局偏移"(FOUT)。@nuxt/fonts 通过自动计算 size-adjust 来最小化这种偏移
字体预加载
@nuxt/fonts 自动预加载首屏需要的字体,无需手动配置。
如果需要手动预加载特定字体:
<script setup>
useHead({
link: [
{
rel: 'preload',
href: '/fonts/inter-var.woff2',
as: 'font',
type: 'font/woff2',
crossorigin: '',
},
],
})
</script>预加载的原理
<link rel="preload"> 告诉浏览器"这个资源马上会用到,请提前下载"。字体预加载可以跳过 CSS 解析 → 发现字体 → 下载的瀑布流,直接并行下载字体。
字体格式选择
| 格式 | 压缩率 | 浏览器支持 | 推荐 |
|---|---|---|---|
| WOFF2 | 最优 | 97%+ | ✅ 首选 |
| WOFF | 较好 | 99%+ | 降级方案 |
| TTF/OTF | 无压缩 | 100% | 源文件,不推荐直接使用 |
| EOT | 最差 | 仅 IE | ❌ 已弃用 |
只提供 WOFF2 格式即可
WOFF2 支持率 97%+,不支持的浏览器会使用 font-display: swap 的系统字体。
Tailwind CSS 字体配置
配合 Tailwind CSS 使用自定义字体:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss', '@nuxt/fonts'],
fonts: {
families: [
{ name: 'Inter', provider: 'google' },
],
},
})// tailwind.config.ts
export default {
theme: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
},
}<template>
<!-- 使用 Tailwind 的 font-sans(即 Inter) -->
<p class="font-sans text-lg">这段文字使用 Inter 字体</p>
<!-- 使用 font-mono -->
<code class="font-mono">console.log('hello')</code>
</template>中文字体优化
中文字体文件通常很大(5-20MB),直接加载会严重影响性能。
方案 1:使用 Google Fonts 的 Noto Sans SC
export default defineNuxtConfig({
fonts: {
families: [
{ name: 'Noto Sans SC', provider: 'google', weights: [400, 700] },
],
},
})Google Fonts 的中文字体优化
Google Fonts 会自动将字体拆分为多个子集(按 Unicode 范围),浏览器只下载页面中实际使用的字符子集。
方案 2:字体子集化
使用 fonttools 或在线工具,只保留需要的字符:
# 安装 fonttools
pip install fonttools brotli
# 生成子集(只包含常用汉字)
pyftsubset NotoSansSC-Regular.otf \
--output-file=NotoSansSC-Regular.subset.woff2 \
--flavor=woff2 \
--layout-features='*' \
--text='常用汉字字符集...'方案 3:系统字体作为 Fallback
body {
/* 自定义字体 + 系统中文字体作为 fallback */
font-family: 'Inter', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}中文项目推荐
使用 Google Fonts 的 Noto Sans SC + 系统中文字体作为 fallback。如果追求极致性能,只使用系统中文字体。
常见问题
1. 字体闪烁(FOIT/FOUT)
- FOIT(Flash of Invisible Text):字体加载前文本不可见
- FOUT(Flash of Unstyled Text):字体加载前用系统字体,加载后切换
// @nuxt/fonts 默认使用 swap,先显示系统字体
// 如果仍然闪烁,检查是否正确配置了 font-display
export default defineNuxtConfig({
fonts: {
defaults: {
display: 'swap', // 确保使用 swap
},
},
})2. 字体导致布局偏移(CLS)
/* @nuxt/fonts 自动计算 size-adjust,但如果手动配置,需要加上 */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
/* 手动设置 size-adjust 减少偏移 */
size-adjust: 100.06%;
}3. Google Fonts 在中国加载慢
@nuxt/fonts 在构建时从 Google 下载字体元数据和文件(不是用户浏览器请求),如果构建环境无法访问 fonts.google.com,会报 Connect Timeout Error 错误。
解决方案(按推荐优先级):
// 方案 1:使用 Fontsource(推荐,零网络依赖)
// npm install @fontsource/inter
// 然后在 app.vue 中 import '@fontsource/inter/400.css'
// 方案 2:禁用 Google provider(不需要 Google 字体时)
export default defineNuxtConfig({
fonts: {
providers: {
google: false, // 禁用 Google Fonts provider
googleicons: false, // 禁用 Google Icons provider
},
},
})
// 方案 3:使用 Bunny Fonts 替代
export default defineNuxtConfig({
fonts: {
families: [
{ name: 'Inter', provider: 'bunny' },
],
},
})
// 方案 4:下载到本地(手动下载 woff2 文件)
// 将 woff2 文件放到 app/assets/fonts/ 目录,使用 provider: 'local'@nuxt/fonts 的构建时 vs 运行时
| 阶段 | 行为 | 网络请求 |
|---|---|---|
构建时(nuxt dev/nuxt build) | 服务端请求 Google Fonts 元数据,下载字体文件到本地 | 需要访问 Google(国内可能超时) |
| 运行时(用户浏览器访问) | 用户从你自己的服务器加载字体 | 不请求 Google |
| 重复访问 | 浏览器缓存字体 | 连你服务器都不请求 |
你遇到的超时错误发生在"构建时"。禁用 provider 或使用 Fontsource 可以避免。
INFO
️ @nuxt/fonts 可能作为其他模块的依赖自动安装(如 @nuxt/ui 内部依赖 @nuxt/fonts) 此时不能通过 npm uninstall 卸载。但可以通过 fonts.providers 禁用特定 provider 来避免网络请求。@nuxt/fonts 没有 enabled: false 选项,禁用 provider 是唯一的关闭方式
知识脉络
CSS 与 SCSS → 资源管理 → 图片优化 → 你在这里:字体
│
├─→ 相关:SEO 与元数据 → Head 管理(字体预加载)
│
└─→ 相关:视图与布局 → 页面(字体与 CLS)