为什么你的英文版比中文版慢 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="/hero-banner.png" ...>
原来 LCP 元素是首屏的大图。
图片使用了 Next.js 的 <Image> 组件,默认是懒加载的:
<Image src="/hero-banner.png" ... />
// 默认 loading="lazy"
但对于首屏图片,懒加载反而拖慢了显示速度。
对 LCP 元素禁用懒加载,并添加 priority 属性:
<Image
src="/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="/card.png" />
// ✅ 正确写法
<img src="/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;
}
性能优化是一个「挤牙膏」的过程:
每一轮看起来改动不大,但累积下来效果惊人。
如果你的多语言网站也有性能问题,不妨按这个顺序排查:
希望这篇文章对你有帮助!