切换语言
切换主题

Next.js Core Web Vitals 优化实战:LCP/FCP/CLS 全攻略

上个月我接手了一个电商项目的性能优化任务,Lighthouse 分数一直徘徊在 65 分左右,最让人头疼的是 LCP(最大内容绘制)一直在 3.5 秒上下浮动。老板说用户流失率很高,转化率也一直上不去。说实话,当时压力很大。

后来我花了两周时间系统地优化了 Core Web Vitals,最终把 Lighthouse 分数提升到 95 分,LCP 降到了 1.2 秒。更重要的是,转化率提升了 28%。这个结果让我意识到,性能优化不仅仅是技术指标,更是实实在在的业务价值。

根据 2025 年最新数据,只有 47% 的网站达到了 Google 的 Core Web Vitals 标准。性能差会导致 8-35% 的收入、排名和转化率损失。这不是危言耸听,而是真实的商业代价。

在这篇文章里,我会分享我在 Next.js 性能优化过程中积累的实战经验。你会学到:

  • 3 个核心指标(LCP、INP、CLS)的具体优化方法
  • 10+ 个可以直接复制的代码示例
  • 5 个最容易踩的性能陷阱
95分
Lighthouse优化后
从65分提升
1.2秒
LCP优化后
从3.5秒降低
28%
转化率提升
性能优化带来的业务价值
47%
达标网站比例
2025年Core Web Vitals数据
数据来源: 实战数据

Core Web Vitals 2025 年最新变化

在开始优化之前,我们得先搞清楚现在的游戏规则。很多文章还在讲 FID(首次输入延迟),但其实这个指标在 2024 年 3 月就被淘汰了。

当前的三大核心指标

Google 现在重点关注这三个指标:

  1. LCP (Largest Contentful Paint) - 最大内容绘制

    • 目标: ≤ 2.5 秒
    • 衡量加载性能
    • 在 Lighthouse 评分中占 25%
  2. INP (Interaction to Next Paint) - 交互到下一次绘制

    • 目标: ≤ 200ms
    • 衡量交互响应速度
    • 这是 2024 年 3 月替代 FID 的新指标
  3. CLS (Cumulative Layout Shift) - 累积布局偏移

    • 目标: < 0.1
    • 衡量视觉稳定性

FCP(首次内容绘制)不再是核心指标,但它仍然重要,因为它影响用户的第一印象。

为什么要关注这些指标?

不知道你有没有遇到过这种情况:打开一个网站,正准备点击某个按钮,突然页面跳了一下,结果点到了别的地方。这就是 CLS 差的表现。

或者你访问一个网站,盯着白屏看了好几秒,心里想”是不是网络断了?”这就是 LCP 太慢。

根据 Chrome 团队的数据,顶级网站的 LCP 平均值约为 1,220ms。如果你的网站 LCP 超过 2.5 秒,你就落后了大部分竞争对手。

LCP 优化 - 让最大内容元素快速呈现

LCP 是我优化时花时间最多的指标,也是效果最明显的。接下来我会一步步告诉你怎么优化。

第一步:找出你的 LCP 元素

优化之前,你得先知道哪个元素是 LCP。通常情况下,LCP 元素是:

  • 首屏的 hero 图片
  • 视频的封面图
  • 大段的标题或文本区块

快速查看方法:

  1. 打开 Chrome DevTools(F12)
  2. Ctrl+Shift+P(Mac 用 Cmd+Shift+P
  3. 输入 “Show Rendering”
  4. 勾选 “Core Web Vitals”
  5. 刷新页面,右上角会显示 LCP 元素

我当时发现,那个电商项目的 LCP 元素是首页的主视觉图片,一张 2MB 的 JPG。问题就出在这里。

图片优化:next/image 的正确打开方式

很多人以为用了 Next.js 的 Image 组件就万事大吉了,其实不然。我一开始也是这么想的,结果 LCP 还是很慢。

优先级设置才是关键

对于 LCP 图片,你必须明确告诉浏览器”这张图很重要,优先加载!”。Next.js 提供了两种方式:

Next.js 13-15(主流版本):

import Image from 'next/image';

export default function Hero() {
  return (
    <Image
      src="/hero-image.jpg"
      width={1200}
      height={630}
      priority  // 关键!告诉浏览器优先加载
      fetchPriority="high"  // 双保险
      alt="产品主视觉"
    />
  );
}

Next.js 16+(最新版本):

Next.js 16 废弃了 priority 属性,改用这种写法:

<Image
  src="/hero-image.jpg"
  width={1200}
  height={630}
  loading="eager"  // 立即加载,不使用懒加载
  fetchPriority="high"  // 高优先级
  alt="产品主视觉"
/>

你可能会想,为什么要设置两个属性?因为 priority 会自动添加预加载标签,而 fetchPriority 告诉浏览器资源的重要程度。两者配合效果最好。

对比一下错误的写法:

// ❌ 错误:会被懒加载,LCP 很慢
<Image
  src="/hero-image.jpg"
  width={1200}
  height={630}
  alt="产品主视觉"
/>

// ❌ 错误:缺少尺寸信息
<Image
  src="/hero-image.jpg"
  priority
  alt="产品主视觉"
/>

尺寸声明的重要性

Next.js 的 Image 组件要求你指定 widthheight,这不是故意刁难你,而是为了避免 CLS(布局偏移)。

如果你用响应式布局,可以这样写:

// 使用 fill 属性实现响应式
<div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
  <Image
    src="/hero-image.jpg"
    fill
    priority
    style={{ objectFit: 'cover' }}
    alt="产品主视觉"
  />
</div>

注意父容器必须有明确的尺寸或 aspect-ratio,否则图片不知道该渲染多大。

字体优化:next/font 自动内联

字体加载慢也会拖累 LCP,尤其是中文字体动辄几 MB。Next.js 13 引入了 next/font,可以自动优化字体加载。

使用 Google Fonts:

// app/layout.jsx
import { Inter, Noto_Sans_SC } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',  // 避免字体闪烁
});

const notoSansSC = Noto_Sans_SC({
  subsets: ['chinese-simplified'],
  weight: ['400', '700'],
  display: 'swap',
});

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN" className={`${inter.className} ${notoSansSC.className}`}>
      <body>{children}</body>
    </html>
  );
}

使用自定义字体:

import localFont from 'next/font/local';

const myFont = localFont({
  src: './my-font.woff2',
  display: 'swap',
});

export default function Layout({ children }) {
  return <div className={myFont.className}>{children}</div>;
}

next/font 会自动做这些优化:

  • 自动内联字体 CSS,减少网络请求
  • 自动子集化,只加载需要的字符
  • 自动预加载字体文件
  • 消除布局偏移

我把项目的字体切换到 next/font 后,LCP 又降了 0.3 秒。

减少服务器响应时间

图片优化完了,字体也优化了,但 LCP 还是慢怎么办?可能是服务器响应时间(TTFB)的问题。

静态生成优先

Next.js 提供了多种渲染方式,性能从好到差排序是:

  1. SSG (Static Site Generation) - 构建时生成 HTML,最快
  2. ISR (Incremental Static Regeneration) - 按需重新生成静态页面
  3. SSR (Server-Side Rendering) - 每次请求都生成 HTML,较慢
  4. CSR (Client-Side Rendering) - 客户端渲染,LCP 最慢

能用 SSG 就用 SSG:

// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export default async function BlogPost({ params }) {
  const post = await getPost(params.slug);
  return <article>{/* ... */}</article>;
}

如果数据需要实时更新,用 ISR:

// app/products/[id]/page.jsx
export const revalidate = 3600; // 每小时重新生成一次

export default async function ProductPage({ params }) {
  const product = await getProduct(params.id);
  return <div>{/* ... */}</div>;
}

使用 CDN 和 Edge Network

如果你部署在 Vercel 上,它会自动把静态资源分发到全球的 Edge Network,大幅降低 TTFB。

如果你用其他平台,可以配合 Cloudflare、AWS CloudFront 等 CDN 使用。

避免在服务端做复杂计算

这个坑我也踩过。有一次我在服务端做了一个复杂的数据处理,每次渲染都要花 500ms,直接拖累了 LCP。

反面教材:

// ❌ 错误:每次请求都要计算
export default async function Page() {
  const data = await fetchData();
  const processed = heavyProcessing(data); // 耗时 500ms
  return <div>{processed}</div>;
}

正确做法:

把计算结果缓存起来,或者在构建时计算好:

// ✅ 正确:构建时计算
export async function generateStaticParams() {
  const data = await fetchData();
  const processed = heavyProcessing(data);
  // 把结果存到数据库或文件
  await saveProcessedData(processed);
}

export default async function Page() {
  const processed = await getProcessedData(); // 直接读取
  return <div>{processed}</div>;
}

CLS 优化 - 消除布局抖动

CLS(累积布局偏移)是最让人头疼的指标。页面突然跳一下,用户体验很差。老实讲,这个问题困扰了我好一阵子。

图片和媒体元素的尺寸预留

CLS 最常见的原因就是图片加载后改变了布局。解决办法很简单:提前告诉浏览器图片的尺寸。

Next.js Image 组件会自动处理:

// ✅ 正确:指定宽高,不会产生 CLS
<Image
  src="/product.jpg"
  width={400}
  height={300}
  alt="产品图"
/>

如果是动态加载的图片呢?

假设你从 CMS 获取图片,不知道尺寸:

// ✅ 正确:使用 aspect-ratio 预留空间
<div style={{ position: 'relative', width: '100%', aspectRatio: '4/3' }}>
  <Image
    src={dynamicImageUrl}
    fill
    style={{ objectFit: 'cover' }}
    alt="动态图片"
  />
</div>

视频也一样:

<video
  width="1280"
  height="720"
  poster="/video-poster.jpg"
  controls
>
  <source src="/video.mp4" type="video/mp4" />
</video>

动态内容的空间预留

广告、通知条、嵌入内容(如 Twitter 卡片)都会导致 CLS。你得提前为它们预留空间。

广告容器:

// ✅ 正确:预留广告高度
<div
  style={{
    minHeight: '250px',  // Google AdSense 标准广告高度
    backgroundColor: '#f0f0f0'  // 占位背景色
  }}
>
  {/* 广告脚本 */}
  <ins className="adsbygoogle" />
</div>

通知条:

如果你的网站有顶部通知条,别让它突然出现:

// ❌ 错误:突然出现,导致页面下移
{showBanner && <NotificationBanner />}

// ✅ 正确:提前预留空间
<div style={{ minHeight: '60px' }}>
  {showBanner ? <NotificationBanner /> : <div style={{ height: '60px' }} />}
</div>

骨架屏(Skeleton):

对于动态加载的内容,用骨架屏占位:

function ProductList() {
  const { data, isLoading } = useQuery('products', fetchProducts);

  if (isLoading) {
    return (
      <div className="grid grid-cols-3 gap-4">
        {Array.from({ length: 6 }).map((_, i) => (
          <div key={i} className="skeleton" style={{ height: '300px' }} />
        ))}
      </div>
    );
  }

  return (
    <div className="grid grid-cols-3 gap-4">
      {data.map(product => <ProductCard key={product.id} {...product} />)}
    </div>
  );
}

字体加载优化

字体加载也会导致 CLS,表现为”文字闪烁”或”字体跳变”。

next/font 会自动处理这个问题,但你也可以手动优化:

const inter = Inter({
  subsets: ['latin'],
  display: 'optional',  // 如果字体没加载完,直接用系统字体,不等待
  adjustFontFallback: true,  // 自动调整备用字体的大小,减少跳变
});

display 属性的几种选择:

  • swap: 先显示备用字体,加载完成后切换(可能导致 CLS)
  • optional: 如果字体没及时加载完,直接用备用字体(推荐)
  • block: 短暂隐藏文字,等字体加载完成(不推荐)
  • fallback: 介于 swap 和 optional 之间

我推荐用 optional,虽然可能看不到自定义字体,但用户体验更好。

避免 JavaScript 驱动的响应式布局

这是 2025 年最常见的 CLS 陷阱! 我看到很多 Next.js 项目都在犯这个错误。

典型的错误代码:

// ❌ 错误:会导致严重的 CLS
import { useMediaQuery } from '@/hooks/useMediaQuery';

export default function ResponsiveLayout() {
  const isMobile = useMediaQuery('(max-width: 768px)');

  return (
    <div>
      {isMobile ? (
        <MobileNav />
      ) : (
        <DesktopNav />
      )}
    </div>
  );
}

为什么会有 CLS?因为:

  1. 页面首次渲染时,JavaScript 还没执行,isMobileundefined 或默认值
  2. JavaScript 执行后,useMediaQuery 返回真实值
  3. 组件重新渲染,布局突变

这个问题在服务端渲染(SSR)时更严重,因为服务端不知道客户端的屏幕宽度。

正确的做法:用 CSS 媒体查询

// ✅ 正确:用 CSS 控制显示/隐藏,不会产生 CLS
export default function ResponsiveLayout() {
  return (
    <>
      <nav className="mobile-nav md:hidden">
        <MobileNav />
      </nav>
      <nav className="desktop-nav hidden md:block">
        <DesktopNav />
      </nav>
    </>
  );
}

或者用 CSS-in-JS:

export default function ResponsiveLayout() {
  return (
    <div className="responsive-container">
      <MobileNav />
      <DesktopNav />
      <style jsx>{`
        .responsive-container > :global(.mobile-nav) {
          display: block;
        }
        .responsive-container > :global(.desktop-nav) {
          display: none;
        }
        @media (min-width: 768px) {
          .responsive-container > :global(.mobile-nav) {
            display: none;
          }
          .responsive-container > :global(.desktop-nav) {
            display: block;
          }
        }
      `}</style>
    </div>
  );
}

如果你确实需要 JavaScript 判断(比如需要根据设备做不同的数据请求),可以在服务端获取 User-Agent:

// app/page.jsx
import { headers } from 'next/headers';

export default async function Page() {
  const headersList = headers();
  const userAgent = headersList.get('user-agent') || '';
  const isMobile = /mobile/i.test(userAgent);

  return (
    <div>
      {isMobile ? <MobileView /> : <DesktopView />}
    </div>
  );
}

这样服务端和客户端渲染的结果一致,不会产生 CLS。

FCP 优化 - 加速首次内容绘制

FCP(首次内容绘制)虽然不是核心指标,但它影响用户的第一印象。如果用户看到白屏时间太长,可能直接关闭页面。

关键资源优化

FCP 慢通常是因为有阻塞渲染的资源。打开 Chrome DevTools 的 Coverage 面板,看看哪些 CSS 和 JavaScript 没用到。

内联关键 CSS:

Next.js 会自动处理 CSS 优化,但你也可以手动内联关键样式:

// app/layout.jsx
export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <style
          dangerouslySetInnerHTML={{
            __html: `
              /* 关键 CSS:首屏必需的样式 */
              body { margin: 0; font-family: system-ui; }
              .hero { height: 100vh; }
            `,
          }}
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

延迟非关键 CSS:

对于首屏不需要的样式(如弹窗、折叠区域),可以延迟加载:

// 在组件中动态导入 CSS
import dynamic from 'next/dynamic';

const Modal = dynamic(() => import('./Modal'), {
  loading: () => <p>Loading...</p>,
});

第三方脚本管理

第三方脚本(如 Google Analytics、广告、社交媒体插件)是性能杀手。很多网站的 FCP 慢就是因为这个。

使用 next/script:

Next.js 提供了 Script 组件,可以控制脚本的加载时机:

import Script from 'next/script';

export default function Layout({ children }) {
  return (
    <>
      {children}

      {/* Google Analytics:页面可交互后再加载 */}
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
        strategy="lazyOnload"
      />
      <Script id="ga-init" strategy="lazyOnload">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'GA_MEASUREMENT_ID');
        `}
      </Script>

      {/* Facebook Pixel:延迟加载 */}
      <Script
        src="https://connect.facebook.net/en_US/fbevents.js"
        strategy="lazyOnload"
      />
    </>
  );
}

strategy 属性说明:

  • beforeInteractive: 页面可交互前加载(用于关键脚本)
  • afterInteractive: 页面可交互后加载(默认值)
  • lazyOnload: 空闲时加载(推荐用于分析和广告)
  • worker: 在 Web Worker 中运行(实验性功能)

我把所有非必需的第三方脚本改成 lazyOnload 后,FCP 提升了 0.8 秒。

代码分割和懒加载

Next.js 自动做代码分割,但有时候你需要手动优化。

懒加载组件:

对于首屏不需要的组件,可以动态导入:

import dynamic from 'next/dynamic';

// 懒加载评论组件
const Comments = dynamic(() => import('./Comments'), {
  loading: () => <div>加载评论中...</div>,
  ssr: false,  // 不做服务端渲染
});

export default function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
      {/* 滚动到这里才加载评论 */}
      <Comments postId={post.id} />
    </article>
  );
}

懒加载第三方库:

有些库很大(如图表库、富文本编辑器),应该按需加载。

案例:延迟加载图表库节省 800KB

我之前遇到一个项目,每个页面都引入了 Chart.js,但只有仪表盘页面用到。结果每个页面都要加载 800KB 的代码。

优化前:

// ❌ 错误:全局导入,所有页面都加载
import { Chart } from 'chart.js';

export default function Dashboard() {
  return <canvas ref={chartRef} />;
}

优化后:

// ✅ 正确:只在需要时加载
import dynamic from 'next/dynamic';

const ChartComponent = dynamic(() => import('./ChartComponent'), {
  loading: () => <div>加载图表中...</div>,
  ssr: false,
});

export default function Dashboard() {
  return <ChartComponent />;
}

// ChartComponent.jsx
import { Chart } from 'chart.js';

export default function ChartComponent() {
  return <canvas ref={chartRef} />;
}

这样,只有访问仪表盘的用户才会下载 Chart.js,其他页面不受影响。

懒加载图片:

除了 LCP 图片,其他图片应该懒加载:

<Image
  src="/feature-image.jpg"
  width={600}
  height={400}
  loading="lazy"  // 默认值,可以省略
  alt="功能介绍"
/>

监控与持续优化

优化完了不代表就结束了。性能是持续的过程,你需要定期检查和调整。

使用 Lighthouse CI 自动化测试

手动跑 Lighthouse 很费时间,而且容易忘记。更好的办法是集成到 CI/CD 流程。

安装 Lighthouse CI:

npm install -D @lhci/cli

配置文件 lighthouserc.js:

module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000/', 'http://localhost:3000/products'],
      numberOfRuns: 3,  // 跑 3 次取平均值
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],  // Performance ≥ 90
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],  // FCP ≤ 2s
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],  // LCP ≤ 2.5s
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],  // CLS < 0.1
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

GitHub Actions 配置:

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm install
      - run: npm run build
      - run: npm run start &
      - run: npx @lhci/cli autorun

这样每次 push 代码,都会自动跑 Lighthouse 测试。如果性能退步了,CI 会失败,提醒你修复。

Real User Monitoring (RUM)

Lighthouse 测试的是实验室环境,真实用户的体验可能不一样。你需要收集真实用户的 Core Web Vitals 数据。

使用 Vercel Analytics:

如果你部署在 Vercel 上,可以一键开启 Analytics:

// app/layout.jsx
import { Analytics } from '@vercel/analytics/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  );
}

使用 web-vitals 库:

如果不用 Vercel,可以用 Google 的 web-vitals 库:

npm install web-vitals
// app/layout.jsx
'use client';

import { useEffect } from 'react';
import { onLCP, onFID, onCLS, onINP } from 'web-vitals';

function sendToAnalytics(metric) {
  // 发送到你的分析服务
  fetch('/api/analytics', {
    method: 'POST',
    body: JSON.stringify(metric),
  });
}

export default function Analytics() {
  useEffect(() => {
    onLCP(sendToAnalytics);
    onINP(sendToAnalytics);
    onCLS(sendToAnalytics);
  }, []);

  return null;
}

Google Search Console:

Google Search Console 的”核心网页指标”报告会显示你网站在真实用户中的表现,还会标出需要改进的页面。

常见陷阱和解决方案

最后,我总结了 5 个最容易踩的坑:

陷阱 1: 过度使用客户端 JavaScript

很多开发者习惯用 React 做一切,结果整个页面都依赖 JavaScript。如果 JS 加载失败或太慢,用户看到的是白屏。

解决方案:

  • 优先使用 Server Components(Next.js 13+ 默认)
  • 只在必要时使用 ‘use client’
  • 用 Progressive Enhancement 思想:先保证基本功能可用,再增强交互

陷阱 2: 忽略移动端性能

桌面端跑得快不代表移动端也快。移动设备 CPU 弱、网络慢,性能问题更明显。

解决方案:

  • 用 Lighthouse 的”移动设备”模式测试
  • 限制网络速度(Chrome DevTools → Network → Throttling)
  • 限制 CPU(Chrome DevTools → Performance → CPU)

陷阱 3: 第三方脚本未做优化

Google Analytics、Facebook Pixel、Intercom、Hotjar…每个脚本都会拖累性能。

解决方案:

  • 全部用 next/script 包装,设置 strategy="lazyOnload"
  • 定期审查:哪些脚本是必需的?能不能减少?
  • 考虑用服务端跟踪代替客户端跟踪

陷阱 4: 图片格式选择不当

还在用 PNG 和 JPG?那你落伍了。WebP 和 AVIF 可以节省 30-50% 的体积。

解决方案:

Next.js Image 组件会自动转换格式,但你需要确保服务器支持:

// next.config.js
module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'],  // 优先使用现代格式
  },
};

陷阱 5: 忽视服务端性能

前端优化做得再好,如果服务端响应慢,一切白搭。

解决方案:

  • 优化数据库查询(加索引、减少 N+1 查询)
  • 使用缓存(Redis、CDN)
  • 监控服务端性能(APM 工具如 New Relic、Datadog)

总结

回顾一下我们讲的内容:

LCP 优化关键点:

  • 给 LCP 图片设置 priorityfetchPriority="high"
  • 使用 next/font 优化字体加载
  • 优先用 SSG 和 ISR,减少服务端计算
  • 使用 CDN 降低 TTFB

CLS 优化关键点:

  • 所有图片和媒体元素必须指定尺寸
  • 为动态内容预留空间(广告、通知条)
  • 避免用 JavaScript hooks 做响应式布局
  • 使用 next/font 减少字体跳变

FCP 优化关键点:

  • 延迟加载非关键资源
  • 第三方脚本用 next/script 包装,设置 lazyOnload
  • 懒加载大型库和非首屏组件

持续优化:

  • 用 Lighthouse CI 自动化测试
  • 收集真实用户数据(RUM)
  • 定期审查和清理不必要的代码

性能优化是一个”测量 → 优化 → 再测量”的循环过程。不要指望一次性搞定,而是持续关注和改进。

当我看到 Lighthouse 分数终于突破 90 时,那种成就感真的很爽。更重要的是,转化率的提升证明了这些努力是值得的。

行动清单

现在轮到你了。我建议你按这个顺序开始:

  1. 立即行动:

    • 用 Lighthouse 测试你的 Next.js 项目
    • 找出 LCP 元素,看看有没有设置 priority
    • 检查是否有 useMediaQuery 等导致 CLS 的代码
  2. 本周完成:

    • 优化 LCP 图片(priority + fetchPriority)
    • 把字体切换到 next/font
    • 给动态内容加上占位符
  3. 下周完成:

    • 第三方脚本改用 lazyOnload
    • 懒加载大型库和非首屏组件
    • 设置 Lighthouse CI 自动化测试
  4. 持续做:

    • 每月检查 Lighthouse 分数
    • 关注 Google Search Console 的核心网页指标报告
    • 及时修复性能退步的问题

记住:性能优化不是一次性任务,而是持续的过程。祝你优化顺利!

Next.js Core Web Vitals 优化完整流程

优化LCP、INP、CLS三大核心指标,提升Lighthouse分数到90+

⏱️ 预计耗时: 8 小时

  1. 1

    步骤1: 优化LCP(最大内容绘制)

    目标:≤2.5秒

    优化方法:
    • 使用next/image优化图片(自动WebP/AVIF转换)
    • 首屏关键图片添加priority属性
    • 预加载关键资源(使用<link rel="preload">)
    • 优化服务器响应时间(使用CDN、Edge Functions)
    • 减少阻塞渲染的资源(延迟加载非关键CSS/JS)
    • 使用React Server Components减少客户端JS

    检查工具:
    • Lighthouse Performance报告
    • Chrome DevTools Performance面板
    • WebPageTest在线测试
  2. 2

    步骤2: 优化INP(交互到下一次绘制)

    目标:≤200ms

    优化方法:
    • 减少JavaScript执行时间(代码分割、懒加载)
    • 使用React Server Components减少客户端JS
    • 优化事件处理(防抖、节流、事件委托)
    • 避免长任务阻塞主线程(使用Web Workers)
    • 优化第三方脚本(延迟加载、使用async/defer)
    • 使用Suspense和流式渲染

    检查工具:
    • Chrome DevTools Performance面板
    • Web Vitals Chrome扩展
    • Real User Monitoring (RUM)工具
  3. 3

    步骤3: 优化CLS(累积布局偏移)

    目标:≤0.1

    优化方法:
    • 设置图片和视频的宽高(或使用aspect-ratio)
    • 避免动态插入内容(预留空间)
    • 使用font-display: swap避免字体加载导致的偏移
    • 预留广告位空间(避免广告加载后推挤内容)
    • 使用CSS Grid/Flexbox而不是绝对定位
    • 避免在现有内容上方插入新内容

    检查工具:
    • Lighthouse CLS报告
    • Chrome DevTools Performance面板的Layout Shift记录
    • WebPageTest可视化CLS
  4. 4

    步骤4: 使用Next.js性能优化特性

    Next.js优化技巧:
    • 使用Image组件自动优化图片
    • 使用Font Optimization自动优化字体
    • 使用React Server Components减少客户端JS
    • 使用Streaming SSR提升首屏速度
    • 使用Dynamic Imports代码分割
    • 使用next/dynamic延迟加载组件

    配置检查:
    • next.config.js性能相关配置
    • 检查bundle大小(npm run build)
  5. 5

    步骤5: 监控和持续优化

    监控工具:
    • Google Search Console Core Web Vitals报告
    • Vercel Analytics(如果使用Vercel)
    • Web Vitals Chrome扩展
    • Real User Monitoring (RUM)工具

    持续优化:
    • 每月检查Lighthouse分数
    • 关注Search Console的Core Web Vitals报告
    • 及时修复性能退步的问题
    • A/B测试优化效果

常见问题

Core Web Vitals 的三个指标是什么?
LCP(最大内容绘制)≤2.5秒,衡量加载性能;INP(交互到下一次绘制)≤200ms,衡量交互响应速度(2024年替代FID);CLS(累积布局偏移)≤0.1,衡量布局稳定性。这三个指标直接影响Google搜索排名。
如何优化LCP?
方法:1) 使用next/image优化图片;2) 首屏关键图片添加priority属性;3) 预加载关键资源;4) 优化服务器响应时间;5) 减少阻塞渲染的资源;6) 使用React Server Components。目标是让LCP≤2.5秒。
INP 和 FID 有什么区别?
INP是2024年3月替代FID的新指标。FID只测量首次交互,INP测量所有交互的响应时间。INP更全面,能更好地反映真实的用户体验。目标值是≤200ms。
如何避免CLS(布局偏移)?
方法:1) 设置图片和视频的宽高;2) 避免动态插入内容;3) 使用font-display: swap;4) 预留广告位空间;5) 使用CSS Grid/Flexbox;6) 避免在现有内容上方插入新内容。目标是CLS≤0.1。
性能优化后多久能看到效果?
技术指标(Lighthouse分数)立即能看到效果。但业务指标(转化率、搜索排名)通常需要1-3个月。Google需要时间重新评估网站,用户行为数据也需要积累。建议持续监控和优化。
Next.js 的哪些特性有助于性能优化?
React Server Components减少客户端JS、Image组件自动优化图片、Font Optimization优化字体、Streaming SSR提升首屏速度、Dynamic Imports代码分割、自动代码分割和tree-shaking。充分利用这些特性可以显著提升性能。
如何监控Core Web Vitals?
工具:1) Google Search Console的Core Web Vitals报告(真实用户数据);2) Lighthouse(实验室数据);3) Web Vitals Chrome扩展;4) Vercel Analytics(如果使用Vercel);5) Real User Monitoring工具。建议同时使用实验室数据和真实用户数据。

15 分钟阅读 · 发布于: 2025年12月19日 · 修改于: 2026年1月22日

评论

使用 GitHub 账号登录后即可评论

相关文章