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.woff2CustomFont、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 官方文档整理编写