Skip to content

字体 ​

@nuxt/fonts 模块自动优化字体加载,避免布局偏移(FOIT/FOUT),提升 Core Web Vitals 评分。

为什么需要字体优化? ​

问题未优化优化后(@nuxt/fonts)
FOIT(不可见文本闪烁)自定义字体加载前文本不可见先显示系统字体,自定义字体加载后替换
CLS(布局偏移)字体切换时页面跳动自动计算 size-adjust,零偏移
请求瀑布流CSS 解析 → 发现字体 → 下载 → 显示预加载关键字体,减少等待
本地字体管理手动配置 @font-face自动检测 assets/fonts/ 中的字体
重复下载每次访问都下载浏览器缓存 + 本地代理

字体加载如何影响性能?

  1. 浏览器解析 HTML → 发现 CSS 引用
  2. 下载 CSS → 解析发现 @font-face
  3. 下载字体文件(可能数百 KB) → 文本渲染被阻塞
  4. 在字体加载完成前,浏览器要么隐藏文本(FOIT),要么用系统字体(FOUT)

@nuxt/fonts 通过预加载、本地代理、size-adjust 等技术优化这个过程。

安装 ​

bash
npm install @nuxt/fonts
ts
export default defineNuxtConfig({
  modules: ['@nuxt/fonts'],
})

@nuxt/fonts 做了什么?

  1. 自动检测 CSS 中的 font-family 引用
  2. 从 Google Fonts 等 Provider 下载字体文件
  3. 代理字体请求到本地(避免 DNS 查询和跨域延迟)
  4. 自动生成 @font-face 声明
  5. 预加载首屏关键字体
  6. 自动计算 size-adjust 减少布局偏移

使用 Google Fonts ​

ts
export default defineNuxtConfig({
  fonts: {
    families: [
      { name: 'Inter', provider: 'google', weights: [400, 500, 700] },
      { name: 'Noto Sans SC', provider: 'google', weights: [400, 700] },
    ],
  },
})
css
body {
  font-family: 'Inter', 'Noto Sans SC', sans-serif;
}

@nuxt/fonts 会自动处理

  1. 从 Google Fonts 下载字体文件
  2. 代理到本地路径(如 /_fonts/inter-400.woff2)
  3. 生成 @font-face 声明并注入 CSS
  4. 预加载首屏需要的字体

无需手动写 @font-face

@nuxt/fonts 全自动处理。

使用本地字体 ​

将字体文件放在 app/assets/fonts/ 目录中,@nuxt/fonts 会自动检测:

text
app/assets/fonts/
├── custom-font.woff2
└── custom-font-bold.woff2
css
body {
  font-family: 'CustomFont', sans-serif;
}

自动检测的原理

@nuxt/fonts 扫描 assets/fonts/ 目录,根据文件名推断字体名称和样式(如 custom-font-bold.woff2 → CustomFont、weight 700),自动生成 @font-face 声明。

手动配置本地字体 ​

如果自动检测不满足需求:

ts
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:

css
/* 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;
}
css
body {
  font-family: 'CustomFont', sans-serif;
}

INFO

️ 手动 @font-face 的缺点:需要手动管理预加载、size-adjust、缓存策略等 推荐使用 @nuxt/fonts 自动处理

字体 Provider ​

Provider说明适用场景网络需求
googleGoogle Fonts最常用,免费字体丰富需访问 google.com(国内可能不稳定)
bunnyBunny FontsGDPR 友好(不追踪用户)需访问 bunny.net
fontsourceFontsource通过 npm 包提供字体仅需 npm,构建后零网络依赖
fontshareFontshare免费优质字体需访问 fontshare.com
adobeAdobe Fonts订阅了 Adobe 创意云需访问 Adobe 服务
local本地字体文件自定义字体无

Google Fonts vs Bunny Fonts vs Fontsource

  • Google Fonts:字体最全,但在中国访问可能不稳定
  • Bunny Fonts:GDPR 友好,不追踪用户,在中国访问更稳定
  • Fontsource:通过 npm 安装字体包,构建时完全本地化,零外部网络请求,最适合网络受限环境

中国用户推荐

优先使用 Fontsource(零网络依赖)或本地字体,避免 Google Fonts 加载慢的问题。

配置不同 Provider ​

ts
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,无需任何外部网络请求:

bash
# 安装需要的字体包
npm install @fontsource/inter
vue
<!-- app/app.vue 或任意页面 -->
<script setup>
import '@fontsource/inter/400.css'
import '@fontsource/inter/700.css'
</script>
css
body {
  font-family: 'Inter', sans-serif;
}

Fontsource 的优势

字体通过 npm install 下载到 node_modules,构建时完全本地处理。不需要访问 Google Fonts 或任何外部服务,非常适合国内开发环境或容器化部署。

搜索可用字体包:https://fontsource.org/fonts

禁用特定 Provider ​

如果某些 Provider 网络不可用(如 Google Fonts 在中国),可以禁用它们:

ts
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 秒无限图标字体(必须等字体加载)
swap0 秒无限推荐——文本始终可见
fallback100ms3 秒对字体要求较高的场景
optional0 秒0 秒只在缓存可用时使用

font-display: swap(推荐)的行为

  1. 浏览器立即用系统字体渲染文本(文本始终可见)
  2. 自定义字体下载完成后,替换系统字体
  3. 不再有"不可见文本闪烁"(FOIT)

INFO

️ swap 的副作用:字体切换时如果字体的尺寸差异大 会出现"布局偏移"(FOUT)。@nuxt/fonts 通过自动计算 size-adjust 来最小化这种偏移

字体预加载 ​

@nuxt/fonts 自动预加载首屏需要的字体,无需手动配置。

如果需要手动预加载特定字体:

vue
<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 使用自定义字体:

ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss', '@nuxt/fonts'],
  fonts: {
    families: [
      { name: 'Inter', provider: 'google' },
    ],
  },
})
ts
// tailwind.config.ts
export default {
  theme: {
    fontFamily: {
      sans: ['Inter', 'system-ui', 'sans-serif'],
      mono: ['JetBrains Mono', 'monospace'],
    },
  },
}
vue
<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 ​

ts
export default defineNuxtConfig({
  fonts: {
    families: [
      { name: 'Noto Sans SC', provider: 'google', weights: [400, 700] },
    ],
  },
})

Google Fonts 的中文字体优化

Google Fonts 会自动将字体拆分为多个子集(按 Unicode 范围),浏览器只下载页面中实际使用的字符子集。

方案 2:字体子集化 ​

使用 fonttools 或在线工具,只保留需要的字符:

bash
# 安装 fonttools
pip install fonttools brotli

# 生成子集(只包含常用汉字)
pyftsubset NotoSansSC-Regular.otf \
  --output-file=NotoSansSC-Regular.subset.woff2 \
  --flavor=woff2 \
  --layout-features='*' \
  --text='常用汉字字符集...'

方案 3:系统字体作为 Fallback ​

css
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):字体加载前用系统字体,加载后切换
ts
// @nuxt/fonts 默认使用 swap,先显示系统字体
// 如果仍然闪烁,检查是否正确配置了 font-display
export default defineNuxtConfig({
  fonts: {
    defaults: {
      display: 'swap',  // 确保使用 swap
    },
  },
})

2. 字体导致布局偏移(CLS) ​

css
/* @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 错误。

解决方案(按推荐优先级):

ts
// 方案 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 是唯一的关闭方式

知识脉络 ​

text
CSS 与 SCSS → 资源管理 → 图片优化 → 你在这里:字体
                                    │
                                    ├─→ 相关:SEO 与元数据 → Head 管理(字体预加载)
                                    │
                                    └─→ 相关:视图与布局 → 页面(字体与 CLS)

基于 Nuxt 4 官方文档整理编写