Next.js 多语言路由性能优化:从 5.8s LCP 到极致体验 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hazellin549
V2EX    前端开发

Next.js 多语言路由性能优化:从 5.8s LCP 到极致体验

  •  
  •   hazellin549 2 月 20 日 1206 次点击
    这是一个创建于 33 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为什么你的英文版比中文版慢 3 倍?


    01 一个诡异的性能问题

    上周,我在检查网站的性能报告时,发现了一个诡异的现象:

    中文 (默认)
    语言版本 LCP CLS
    2.3s 0.06
    英语 /en/ 5.8s 0.68

    同一套代码,同一个页面,为什么英文版比默认语言慢了整整 2.5 倍?

    这篇文章记录我的排查过程和最终解决方案。


    02 先搞清楚 LCP 是什么

    LCP (Largest Contentful Paint) 是 Core Web Vitals 的核心指标之一。

    简单说,它测量的是:页面主要内容出现的时间

    Google 的标准是:

    • < 2.5s 良好
    • 2.5s - 4s 需要改进
    • > 4s 较差

    我的英文版 5.8s ,妥妥的「红牌警告」。


    03 第一个嫌疑人:缓存策略

    在 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%**。


    04 第二个嫌疑人:关键资源预加载

    优化缓存后,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


    05 第三个嫌疑人:图片加载

    还差 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


    06 CLS 问题:页面为什么在「抖动」?

    解决完 LCP ,再看 CLS 。

    CLS (Cumulative Layout Shift) 测量的是:页面元素的意外位移

    我的英文版 CLS 是 0.68 ,而标准是 < 0.1 。

    打开页面慢动作回放,发现了两个问题:

    问题 1:字体加载导致的「闪烁」(FOUT)

    页面先用系统字体渲染,字体加载完后「闪」一下变成自定义字体。

    由于两种字体的 metrics 不同,文字位置发生偏移。

    解决方案:使用 font-display: optional

    @font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-display: optional; /* 如果字体没及时加载,就不换了 */ } 

    问题 2:图片没有指定尺寸

    // 错误写法 <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


    07 优化前后对比

    指标 优化前 优化后 改善
    LCP 5.8s 2.2s -62%
    CLS 0.68 0.04 -94%
    缓存命中率 31% 85% +174%
    Lighthouse 52 91 +75%

    08 核心经验总结

    多语言路由的性能优化,核心要点:

    缓存策略

    非默认语言路由容易被当作「动态页面」而跳过缓存。显式配置 Cache-Control

    预加载关键资源

    字体和关键 CSS 形成瀑布流是 LCP 的常见杀手。<link rel="preload"> 打破瀑布。

    LCP 元素优先加载

    首屏图片不要懒加载。priority 属性告诉 Next.js 这是关键资源。

    防止布局偏移

    • 图片必须指定尺寸
    • 字体使用 font-display: optionalswap + 预加载
    • 避免动态插入内容到首屏

    09 一个容易忽略的细节

    最后分享一个容易忽略的点:

    不同语言版本的字体 metrics 可能不同。

    比如中文字体通常比英文字体更高,如果你用同一套 CSS ,line-height 可能导致不同语言的布局高度不一致。

    我的做法是为每种语言定义 bodyfont-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; } 

    10 写在最后

    性能优化是一个「挤牙膏」的过程:

    • 第一轮优化(缓存):5.8s → 3.9s
    • 第二轮优化(预加载):3.9s → 2.7s
    • 第三轮优化(图片):2.7s → 2.2s

    每一轮看起来改动不大,但累积下来效果惊人。

    如果你的多语言网站也有性能问题,不妨按这个顺序排查:

    1. 缓存是否生效?
    2. 关键资源是否预加载?
    3. LCP 元素是否优先加载?
    4. 有没有布局偏移?

    希望这篇文章对你有帮助!

    1 条回复    2026-02-21 12:52:30 +08:00
    seven777
        1
    seven777  
       2 月 21 日 via iPhone
    Next.js v16 latest ?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1217 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 23:32 PVG 07:32 LAX 16:32 JFK 19:32
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86