
为什么你的英文版比中文版慢 3 倍?
上周,我在检查网站的性能报告时,发现了一个诡异的现象:
| 语言版本 | LCP | CLS |
|---|---|---|
| 2.3s | 0.06 | |
| 英语 /en/ | 5.8s | 0.68 |
同一套代码,同一个页面,为什么英文版比默认语言慢了整整 2.5 倍?
这篇文章记录我的排查过程和最终解决方案。
LCP (Largest Contentful Paint) 是 Core Web Vitals 的核心指标之一。
简单说,它测量的是:页面主要内容出现的时间。
Google 的标准是:
我的英文版 5.8s ,妥妥的「红牌警告」。
在 Next.js 的多语言路由中,默认语言和非默认语言的处理逻辑是不同的。
默认语言: example.com/product 非默认语言: example.com/en/product 我检查了 CDN 缓存命中率:
| 路由 | 缓存命中率 |
|---|---|
| /product | 89% |
| /en/product | 31% |
问题出现了:非默认语言的缓存命中率低得可怜。
Next.js 默认生成的 Cache-Control 头对于动态路由是这样的:
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate 而我的多语言路由 /[locale]/... 被识别为「动态路由」,导致 CDN 几乎不缓存。
在 next.config.js 中显式配置缓存策略:
async headers() { return [ { source: '/:locale(en|es|fr)/:path*', headers: [ { key: 'Cache-Control', value: 'public, s-maxage=3600, stale-while-revalidate=86400', }, ], }, ]; } 效果:缓存命中率从 31% 提升到 **85%**。
优化缓存后,LCP 从 5.8s 降到了 3.9s ,但还是不达标。
Lighthouse 报告给了一个提示:
Preload key requests Fonts and critical CSS are not being preloaded 我检查了页面的资源加载顺序:
1. HTML 文档 2. 等待解析... 3. 发现 CSS 引用 4. 下载 CSS 5. 发现字体引用 6. 下载字体 7. 渲染文字 ← LCP 发生在这里 问题是:字体要等到 CSS 加载完才开始下载,形成了瀑布流。
在 <head> 中添加预加载标签:
<Head> {/* 预加载关键字体 */} <link rel="preload" href="/fonts/inter-variable.woff2" as="font" type="font/woff2" crossOrigin="anonymous" /> {/* 预加载关键 CSS */} <link rel="preload" href="/_next/static/css/app.css" as="style" /> </Head> 效果:LCP 从 3.9s 降到 2.7s。
还差 0.2s 才能达到 2.5s 的「良好」标准。
Lighthouse 又给了提示:
Largest Contentful Paint element <img src="http://www.v2ex.com/hero-banner.png" ...> 原来 LCP 元素是首屏的大图。
图片使用了 Next.js 的 <Image> 组件,默认是懒加载的:
<Image src="http://www.v2ex.com/hero-banner.png" ... /> // 默认 loading="lazy" 但对于首屏图片,懒加载反而拖慢了显示速度。
对 LCP 元素禁用懒加载,并添加 priority 属性:
<Image src="http://www.v2ex.com/hero-banner.png" priority // 关键! loading="eager" ... /> 效果:LCP 从 2.7s 降到 2.2s
解决完 LCP ,再看 CLS 。
CLS (Cumulative Layout Shift) 测量的是:页面元素的意外位移。
我的英文版 CLS 是 0.68 ,而标准是 < 0.1 。
打开页面慢动作回放,发现了两个问题:
页面先用系统字体渲染,字体加载完后「闪」一下变成自定义字体。
由于两种字体的 metrics 不同,文字位置发生偏移。
解决方案:使用 font-display: optional
@font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-display: optional; /* 如果字体没及时加载,就不换了 */ } // 错误写法 <img src="http://www.v2ex.com/card.png" /> // 正确写法 <img src="http://www.v2ex.com/card.png" width={300} height={400} /> Next.js 的 <Image> 组件会自动处理这个问题,但我有几个地方用了原生 <img>。
效果:CLS 从 0.68 降到 0.04
| 指标 | 优化前 | 优化后 | 改善 |
|---|---|---|---|
| LCP | 5.8s | 2.2s | -62% |
| CLS | 0.68 | 0.04 | -94% |
| 缓存命中率 | 31% | 85% | +174% |
| Lighthouse | 52 | 91 | +75% |
多语言路由的性能优化,核心要点:
非默认语言路由容易被当作「动态页面」而跳过缓存。显式配置 Cache-Control。
字体和关键 CSS 形成瀑布流是 LCP 的常见杀手。用 <link rel="preload"> 打破瀑布。
首屏图片不要懒加载。用 priority 属性告诉 Next.js 这是关键资源。
font-display: optional 或 swap + 预加载最后分享一个容易忽略的点:
不同语言版本的字体 metrics 可能不同。
比如中文字体通常比英文字体更高,如果你用同一套 CSS ,line-height 可能导致不同语言的布局高度不一致。
我的做法是为每种语言定义 body 的 font-family 后备栈:
/* 英语/西班牙语 */ body[lang="en"], body[lang="es"] { font-family: 'Inter', system-ui, sans-serif; } /* 中文 */ body[lang="zh"] { font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif; } 性能优化是一个「挤牙膏」的过程:
每一轮看起来改动不大,但累积下来效果惊人。
如果你的多语言网站也有性能问题,不妨按这个顺序排查:
希望这篇文章对你有帮助!