NextJS 14中next/font/google字体在Safari iOS上无法正常显示
问题还原
在 Next.js 项目中通过 next/font/google 引入 Jomolhari、Josefin_Slab、Josefin_Sans 三款字体,Chrome、Firefox、Mac Safari 均正常渲染,但 iOS Safari 会默认 fallback 到 Times New Roman。已尝试本地字体托管、自定义 @font-face、远程 URL 引入等方案,仍未解决。
用户生产环境代码(存在语法问题):
// layout.tsx import { Jomolhari, Josefin_Slab, Josefin_Sans } from "next/font/google"; const jomolhari = Jomolhari({ weight: "400", subsets: ["latin"], variable: "--font-jomolhari", display: "swap", }); const josefinSans = Josefin_Sans({ subsets: ["latin"], variable: "--font-josefin-sans", display: "swap", }); const josefinSlab = Josefin_Slab({ subsets: ["latin"], export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html className={`${jomolhari.variable} ${josefinSans.variable} ${josefinSlab.variable}`} lang="en" > <body className={josefinSlab.className}> {children} </body> </html> ); } });
解决方案
1. 修复代码语法错误
代码中 josefinSlab 的配置未闭合,且缺少必要参数,同时末尾多了冗余的 });,这会导致字体变量无法正确注册,直接引发 iOS Safari 加载失败。修正后的代码:
import { Jomolhari, Josefin_Slab, Josefin_Sans } from "next/font/google"; const jomolhari = Jomolhari({ weight: "400", subsets: ["latin"], variable: "--font-jomolhari", display: "swap", }); const josefinSans = Josefin_Sans({ weight: ["400", "600"], // 明确指定可用字重 subsets: ["latin"], variable: "--font-josefin-sans", display: "swap", }); const josefinSlab = Josefin_Slab({ weight: ["400", "600"], // iOS Safari 对字重匹配要求严格,必须明确指定 subsets: ["latin"], variable: "--font-josefin-slab", display: "swap", }); export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html className={`${jomolhari.variable} ${josefinSans.variable} ${josefinSlab.variable}`} lang="en" > <body className={josefinSlab.className}> {children} </body> </html> ); }
2. 强制指定字体 fallback 栈
iOS Safari 有时会因字体匹配失败直接跳转系统字体,需在全局 CSS 中明确字体优先级:
/* global.css */ body { font-family: var(--font-josefin-slab), 'Josefin Slab', serif; } /* 其他字体的使用场景同理 */ .jomolhari-text { font-family: var(--font-jomolhari), 'Jomolhari', serif; }
同时包含自定义字体变量、字体官方名称和通用 serif/sans-serif 兜底,避免直接跳到 Times New Roman。
3. 本地托管字体并配置 @font-face
若 next/font 自动处理仍有问题,手动下载字体文件托管到项目 public/fonts 目录,然后在 global.css 中配置:
@font-face { font-family: 'Josefin Slab'; src: url('/fonts/JosefinSlab-Regular.woff2') format('woff2'), url('/fonts/JosefinSlab-Regular.woff') format('woff'); font-weight: 400; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } @font-face { font-family: 'Josefin Slab'; src: url('/fonts/JosefinSlab-SemiBold.woff2') format('woff2'), url('/fonts/JosefinSlab-SemiBold.woff') format('woff'); font-weight: 600; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
这种方式完全避开跨域加载问题,iOS Safari 兼容性更好。
4. 调整字体加载策略
iOS Safari 对 font-display: swap 的处理存在差异,可尝试添加预加载标签(在 RootLayout 的 head 中):
<head> <link rel="preload" href="/fonts/JosefinSlab-Regular.woff2" as="font" type="font/woff2" crossOrigin="anonymous" /> </head>
提前预加载核心字体,减少加载延迟导致的 fallback。
5. 检查 iOS 隐私设置
部分用户开启“阻止跨站跟踪”会影响 Google Fonts 远程加载,本地托管字体可彻底解决此问题。
内容的提问来源于stack exchange,提问作者Ricardo Nazario

