← 全部指南

菜单和 LCP:导航如何阻止最大内容绘制

菜单字体加载:防止布局抖动和文字不可见

font-display 策略用于导航菜单——在品牌一致性与 LCP 和 CLS 之间取得平衡。

你花了数小时选择完美的字体来匹配你的商店。菜单标签使用与品牌完全匹配的自定义无衬线字体。但每次你在手机上测试商店时,你都会看到菜单应该显示的地方出现不可见的文字闪烁,然后当字体最终加载时会发生布局抖动。你的 CLS 分数是 0.18,Google 标记为”需要改进”,你怀疑菜单字体是罪魁祸首。

问题不在字体本身。问题在于浏览器如何处理字体加载。默认情况下,大多数浏览器会隐藏文字,直到自定义字体下载完成,在缓慢的移动连接上这可能需要 300 到 800 毫秒。菜单区域空白显示,页面看起来被损坏了,当字体最终出现时,它会改变下面所有内容的位置。这既伤害了 LCP(因为重要内容被延迟),也伤害了 CLS(因为布局移动了)。

这篇文章解释了五种 font-display 策略、哪一种最适合导航菜单,以及如何实现不会破坏品牌或核心网页关键指标的字体加载。

快速摘要
  • 浏览器默认在字体下载期间隐藏文字,导致不可见文字闪烁 (FOIT)。
  • font-display 控制文字是立即显示还是等待自定义字体。
  • font-display: swap 是菜单文字的最佳选择——立即显示备用字体,准备好时切换。
  • 系统字体(已安装在设备上)有零下载成本,完全消除 FOIT/FOUT。
  • 字体预加载可以减少延迟,但不能完全消除布局抖动的风险。

两个字体加载问题:FOIT 和 FOUT

当浏览器遇到自定义网页字体时,它必须决定如何处理使用该字体的文字,而字体文件仍在下载中。有两种可能的行为,各有优缺点。

不可见文字闪烁 (FOIT)

浏览器隐藏文字,直到字体加载。文字空间被保留,但什么都不显示。如果字体花费 500 毫秒来下载,用户会看到半秒钟的空白。这是大多数浏览器中的默认行为。

FOIT 对 LCP 不利,因为重要的文字内容在字体加载之前不可见。这对可用性也不利——用户无法读取菜单,这可能让他们认为页面被破坏了。

无样式文字闪烁 (FOUT)

浏览器使用备用字体(通常是 Arial 或 Helvetica 等系统字体)立即显示文字,然后在字体加载后切换到自定义字体。文字可以立即阅读,但如果备用字体和自定义字体有不同的尺寸,切换可能会导致布局抖动。

FOUT 对 LCP 更好(文字立即出现),但如果处理不当可能会伤害 CLS。关键是让备用字体的尺寸与自定义字体尽可能接近。

五种 font-display 策略

font-display CSS 属性控制浏览器如何处理字体加载期间。有五个可能的值,各有不同的时序和备用行为。

行为 最适合
auto 浏览器决定(通常 3 秒 FOIT,然后 FOUT) 不推荐——不可预测
block 最多 3 秒 FOIT,然后切换 装饰文字,品牌字体很重要
swap 立即 FOUT——显示备用,准备好时切换 正文、导航菜单
fallback 100ms FOIT,然后 3 秒 FOUT,然后坚持使用备用 标题,字体重要但速度也重要
optional 100ms FOIT,然后如果字体未就绪则使用备用,从不切换 性能关键的文字

对于导航菜单,font-display: swap 几乎总是正确的选择。它使菜单立即可读(对 LCP 和可用性有利),并在字体加载时立即切换到品牌字体(对品牌一致性有利)。风险是布局抖动,我们稍后会解决。

如何在样式表中使用 font-display

如果你的字体在 CSS 中使用 @font-face 定义,添加 font-display 属性:

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

.menu-link {
  font-family: 'BrandFont', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}

font-family 堆栈很重要。首先列出自定义字体,然后是系统字体作为备用。这确保菜单文字立即以系统字体显示,然后在字体加载时切换到自定义字体。

如果你的字体从 Google Fonts 加载,在 URL 中添加 display=swap 参数:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap">

Google Fonts 将自动将 font-display: swap 注入生成的 CSS 中。

使用备用字体匹配防止布局抖动

font-display: swap 的问题是,如果备用字体和自定义字体有不同的大小或间距,当切换发生时文字会重新流动,导致布局抖动。这在导航菜单中特别明显,甚至是很小的移位也会移动整个页面内容。

解决方案是使用 size-adjustascent-overridedescent-override 属性调整备用字体以匹配自定义字体的尺寸。这些是相对较新的 CSS 功能(截至 2024 年在所有现代浏览器中受支持),它让你能够调整备用字体来匹配自定义字体。

这里有一个例子:

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'BrandFont Fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 95%;
  descent-override: 25%;
}

.menu-link {
  font-family: 'BrandFont', 'BrandFont Fallback', Arial, sans-serif;
}

size-adjust 属性缩放备用字体,使其 x 高度与自定义字体相匹配。ascent-overridedescent-override 属性调整垂直间距。

找到正确的值需要测试。Simon Hearne 的 Fallback Font Generator 等工具可以自动化这个过程。你输入自定义字体,该工具计算常见系统字体的最优覆盖值。

以这种方式匹配备用字体可以将 CLS 从字体切换减少到接近零。文字仍会从备用字体切换到自定义字体,但布局不会移动,因为尺寸相同。

系统字体:零成本替代方案

最快的字体是根本没有字体——或者说,已安装在用户设备上的系统字体。系统字体有零下载成本、零解析成本和零布局抖动风险。

这是一个在所有平台上看起来都很好的现代系统字体堆栈:

.menu-link {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', Arial, sans-serif;
}

这为你提供:

  • macOS 和 iOS 上的 San Francisco
  • Windows 上的 Segoe UI
  • Android 上的 Roboto
  • Linux 上的平台特定默认值

系统字体不如自定义字体那么独特,但它们清晰、可读,并针对其平台进行了优化。许多高流量网站(GitHub、Medium、WordPress.com)出于性能原因使用系统字体。

如果你的品牌指南要求自定义字体,考虑为导航菜单使用系统字体,并为标题、英雄文本或正文保留自定义字体,因为它在这些地方有更多的视觉冲击。菜单是功能性 UI。它不需要像标题那样承载品牌。

预加载字体以减少 FOUT 持续时间

即使使用 font-display: swap,备用显示和自定义字体切换之间也有延迟。在缓慢的连接上,这可能是 500 毫秒或更长时间。预加载字体文件告诉浏览器以高优先级尽早获取它,这减少了切换延迟。

<link rel="preload" href="/fonts/brandfont.woff2" as="font" type="font/woff2" crossorigin>

即使字体托管在你自己的域上,crossorigin 属性也是必需的,因为字体是通过 CORS 模式获取的。

预加载不能消除 FOUT,但它减少了持续时间。如果你的菜单字体很小(在 50KB 以下)并且对品牌至关重要,预加载可以改进感知的性能。

注意: 预加载与其他关键资源(如 CSS 和 JavaScript)竞争。只预加载在视口上方使用的字体,这些字体真正是必需的。预加载太多资源会减慢初始渲染。

图标字体 vs 内联 SVG

许多导航菜单使用图标字体(Font Awesome、Remix Icon、Material Icons)来显示箭头、搜索图标和汉堡菜单图标。图标字体就是字体,因此具有与文本字体相同的加载特性:默认 FOIT、布局抖动风险和下载成本(完整图标字体通常为 50-150KB)。

内联 SVG 是菜单图标的更好选择。内联 SVG 是 HTML 的一部分,因此立即出现,无需网络请求,无字体加载延迟。它也能完美缩放并支持 CSS 样式。

这是一个例子:

<button class="menu-toggle" aria-label="Open menu">
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
    <line x1="3" y1="12" x2="21" y2="12"></line>
    <line x1="3" y1="6" x2="21" y2="6"></line>
    <line x1="3" y1="18" x2="21" y2="18"></line>
  </svg>
</button>

这个汉堡菜单图标是 200 字节。一个包含 2000 个你永远不会使用的图标的完整图标字体是 150KB。内联 SVG 也消除了 FOIT 问题——图标立即出现,因为它是 HTML 的一部分。

Navi+ 等现代菜单应用为所有图标使用内联 SVG,这是它们避免困扰基于图标字体的菜单的布局抖动问题的原因之一。

Shopify 特定的字体加载

Shopify 主题通常以三种方式之一加载字体:来自 Google Fonts、来自 Shopify 的 CDN(通过主题设置字体选择器)或来自主题资产文件夹中的自定义字体文件。

Google Fonts

如果你的主题使用 Google Fonts,确保 URL 中有 display=swap 参数。在 theme.liquid 中检查如下所示的行:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600">

将其更改为:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap">

Shopify CDN 字体

如果你的主题使用 Shopify 的内置字体选择器(设置 > 排版),Shopify 从其 CDN 提供字体,并自 2023 年起自动包含 font-display: swap。你不需要做任何事情。

自定义字体文件

如果你将自定义字体文件上传到主题的资产文件夹,在 CSS 中的 @font-face 声明中添加 font-display: swap

衡量字体加载的影响

使用 Chrome DevTools 查看字体如何影响你的 LCP 和 CLS。

检查 FOIT

打开 DevTools,转到网络标签,按”字体”筛选,然后重新加载页面。观察菜单区域。如果菜单文字在出现前有明显的不可见时间段,你有 FOIT。添加 font-display: swap 来修复它。

检查布局抖动

打开 DevTools,转到性能标签,点击记录,重新加载页面,然后停止记录。查看体验部分中的红色”布局抖动”条。点击其中一个查看哪些元素移动了。如果菜单文字被列出,字体切换导致了布局抖动。使用备用字体匹配来修复它。

衡量 CLS

运行 Lighthouse 测试(在 DevTools 或 PageSpeed Insights 中)。查看 CLS 分数和”避免大的布局抖动”诊断。如果字体被列为原因,你需要更好的备用匹配或应考虑切换到系统字体。

快速胜利如果你的菜单使用自定义字体并且你看到 CLS 问题,尝试切换到系统字体堆栈作为测试。再次运行 Lighthouse。如果 CLS 显著下降,自定义字体是问题。然后你可以决定自定义字体的品牌价值是否值得性能成本,或者实现备用匹配以获得两者。

什么是良好的菜单字体加载

优化良好的导航菜单以两种方式之一处理字体。

选项 1:系统字体。 没有自定义字体,零下载成本,零 FOIT,零布局抖动。菜单以原生外观的文字立即出现。

.menu-link {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}

选项 2:带切换和备用匹配的自定义字体。 菜单文字立即以系统字体显示,然后切换到自定义字体而无需布局抖动。

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'BrandFont Fallback';
  src: local('Arial');
  size-adjust: 107%;
}

.menu-link {
  font-family: 'BrandFont', 'BrandFont Fallback', Arial, sans-serif;
}

两种方法都会导致菜单在页面加载的第一秒内可读,这就是 LCP 测量的内容。第一种方法更简单、更快。第二种方法以最小的性能成本保持品牌一致性。

对于图标,使用内联 SVG 而不是图标字体。图标立即出现,完美缩放,成本仅为几百字节,而不是 150KB。

大多数从默认字体加载(FOIT、无备用匹配)切换到这些模式之一的商店会看到 CLS 下降 0.05 到 0.15 个点,LCP 在移动设备上改进 200 到 400 毫秒。对用户来说菜单看起来一样,但浏览器呈现得更快更平稳。

这篇文章是关于更大指南 菜单和 LCP:导航如何阻止最大内容绘制 的一部分。

分享 Facebook X

开始使用 Navi+ AI Menu Builder

选择您的平台 — 免费安装,几分钟内上线。