Next.js 14 性能优化:从首屏加载到运行时优化的最佳实践

在现代 Web 应用中,性能优化直接影响用户体验和业务转化。Next.js 14 提供了多种内置的性能优化特性,今天我们就来深入探讨如何充分利用这些特性,以及一些实用的优化技巧。

图片和字体优化

1. 图片优化

Next.js 的 Image 组件供了强大的图片优化功能:

typescript 复制代码
// components/OptimizedImage.tsx
import Image from 'next/image';
import { useState } from 'react';

interface OptimizedImageProps {
  src: string;
  alt: string;
  width: number;
  height: number;
}

export function OptimizedImage({
  src,
  alt,
  width,
  height
}: OptimizedImageProps) {
  const [isLoading, setLoading] = useState(true);

  return (
    <div className="relative overflow-hidden">
      <Image
        src={src}
        alt={alt}
        width={width}
        height={height}
        quality={75} // 默认图片质量
        placeholder="blur" // 使用模糊占位
        blurDataURL="data:image/jpeg;base64,..." // 生成的 base64 图片
        className={`
          duration-700 ease-in-out
          ${isLoading ? 'scale-110 blur-2xl' : 'scale-100 blur-0'}
        `}
        onLoadingComplete={() => setLoading(false)}
        priority={false} // 是否优先加载
      />
    </div>
  );
}

// 使用自定义图片加载器
const imageLoader = ({ src, width, quality }) => {
  return `https://your-cdn.com/${src}?w=${width}&q=${quality || 75}`;
};

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    loader: 'custom',
    loaderFile: './lib/imageLoader.ts',
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    formats: ['image/webp'],
  },
};

2. 字体优化

Next.js 14 提供了内置的字体优化:

typescript 复制代码
// app/fonts.ts
import { Inter, Roboto_Mono } from 'next/font/google';

export const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  preload: true,
  fallback: ['system-ui', 'arial'],
  adjustFontFallback: true, // 自动调整回退字体
});

export const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
});

// app/layout.tsx
import { inter } from './fonts';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

// 自定义字体加载
import localFont from 'next/font/local';

const myFont = localFont({
  src: [
    {
      path: '../public/fonts/font-regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: '../public/fonts/font-bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  display: 'swap',
  preload: true,
});

动态导入和代码分割

1. 组件动态导入

typescript 复制代码
// components/DynamicComponent.tsx
import dynamic from 'next/dynamic';
import { Suspense } from 'react';

// 基础动态导入
const DynamicHeader = dynamic(() => import('./Header'), {
  loading: () => <p>Loading...</p>,
  ssr: true, // 是否服务端渲染
});

// 带有自定义加载的动态导入
const DynamicChart = dynamic(
  () => import('./Chart').then(mod => mod.Chart),
  {
    loading: () => <ChartSkeleton />,
    ssr: false, // 禁用服务端渲染
  }
);

// 使用 Suspense 包裹动态组件
export function DashboardPage() {
  return (
    <div>
      <DynamicHeader />
      <Suspense fallback={<ChartSkeleton />}>
        <DynamicChart />
      </Suspense>
    </div>
  );
}

2. 路由分组和懒加载

typescript 复制代码
// app/(marketing)/layout.tsx
import { Suspense } from 'react';

// 营销相关页面的布局
export default function MarketingLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="marketing-layout">
      <Suspense fallback={<NavSkeleton />}>
        <Navigation />
      </Suspense>
      {children}
    </div>
  );
}

// app/(dashboard)/layout.tsx
// 仪表板相关页面的布局
export default function DashboardLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="dashboard-layout">
      <Suspense fallback={<SidebarSkeleton />}>
        <Sidebar />
      </Suspense>
      <main>{children}</main>
    </div>
  );
}

缓存策略优化

1. 数据缓存

typescript 复制代码
// lib/cache.ts
import { cache } from 'react';
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.REDIS_URL,
  token: process.env.REDIS_TOKEN,
});

// 使用 React 缓存
export const getCachedData = cache(async (key: string) => {
  // 首先尝试从 Redis 获取
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  // 如果没有缓存,则获取新数据
  const data = await fetchData(key);

  // 存入 Redis
  await redis.set(key, JSON.stringify(data), {
    ex: 3600 // 1小时过期
  });

  return data;
});

// 使用示例
async function ProductPage({ id }: { id: string }) {
  const product = await getCachedData(`product:${id}`);
  return <ProductDetails product={product} />;
}

2. 静态生成优化

typescript 复制代码
// app/products/[id]/page.tsx
import { generateMetadata } from 'next';

// 生成静态路由
export async function generateStaticParams() {
  const products = await getTopProducts();

  return products.map((product) => ({
    id: product.id,
  }));
}

// 静态元数据
export async function generateMetadata({ params }: {
  params: { id: string }
}): Promise<Metadata> {
  const product = await getProduct(params.id);

  return {
    title: product.name,
    description: product.description,
    openGraph: {
      images: [product.image],
    },
  };
}

// 页面组件
export default async function ProductPage({
  params
}: {
  params: { id: string }
}) {
  const product = await getProduct(params.id);

  return (
    <div>
      <h1>{product.name}</h1>
      <ProductDetails product={product} />
    </div>
  );
}

首屏加载优化

1. 流式渲染

typescript 复制代码
// app/page.tsx
import { Suspense } from 'react';
import { headers } from 'next/headers';

async function SlowComponent() {
  const headersList = headers();
  const userAgent = headersList.get('user-agent');

  // 模拟慢速数据加载
  await new Promise(resolve => setTimeout(resolve, 2000));

  return (
    <div>
      <p>User Agent: {userAgent}</p>
    </div>
  );
}

export default function HomePage() {
  return (
    <div>
      <h1>即时加载的内容</h1>

      <Suspense fallback={<LoadingSkeleton />}>
        <SlowComponent />
      </Suspense>

      <Suspense fallback={<CardsSkeleton />}>
        <PopularProducts />
      </Suspense>

      <Suspense fallback={<FeedSkeleton />}>
        <RecentActivity />
      </Suspense>
    </div>
  );
}

2. 预加载数据

typescript 复制代码
// lib/prefetch.ts
export async function prefetchData() {
  // 预加载关键数据
  const promises = [
    prefetchNavigation(),
    prefetchUserData(),
    prefetchPopularProducts(),
  ];

  await Promise.all(promises);
}

// app/layout.tsx
export default async function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  // 在布局组件中预加载数据
  await prefetchData();

  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

Core Web Vitals 优化

1. 性能监控

typescript 复制代码
// lib/analytics.ts
export function reportWebVitals({
  id,
  name,
  label,
  value,
}: {
  id: string;
  name: string;
  label: string;
  value: number;
}) {
  // 发送性能指标到分析服务
  fetch('/api/analytics', {
    method: 'POST',
    body: JSON.stringify({
      id,
      name,
      label,
      value,
      // 添加其他上下文信息
      page: window.location.pathname,
      timestamp: Date.now(),
    }),
  });
}

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
};

// instrumentation.ts
export function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    // 服务端监控
    require('./monitoring/server').setup();
  }
}

2. 性能优化实践

typescript 复制代码
// components/OptimizedList.tsx
import { useVirtualizer } from '@tanstack/react-virtual';
import { useIntersectionObserver } from '@/hooks/useIntersectionObserver';

// 虚拟列表优化
export function OptimizedList({ items }: { items: any[] }) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50,
  });

  return (
    <div ref={parentRef} className="h-[500px] overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualItem.size}px`,
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            <ListItem item={items[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

// 图片懒加载优化
export function LazyImage({ src, alt }: { src: string; alt: string }) {
  const imgRef = useRef<HTMLImageElement>(null);
  const { isIntersecting } = useIntersectionObserver(imgRef);

  return (
    <img
      ref={imgRef}
      src={isIntersecting ? src : ''}
      alt={alt}
      loading="lazy"
      decoding="async"
    />
  );
}

3. 构建优化

typescript 复制代码
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

/** @type {import('next').NextConfig} */
const nextConfig = {
  // 优化构建配置
  poweredByHeader: false,
  compress: true,
  productionBrowserSourceMaps: false,

  // 优化图片配置
  images: {
    minimumCacheTTL: 60,
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },

  // 实验性功能
  experimental: {
    optimizeCss: true, // 启用 CSS 优化
    scrollRestoration: true, // 启用滚动位置恢复
    serverActions: true, // 启用服务端操作
  },

  // webpack 配置
  webpack: (config, { dev, isServer }) => {
    // 优化生产环境构建
    if (!dev && !isServer) {
      config.optimization.splitChunks = {
        chunks: 'all',
        minSize: 20000,
        maxSize: 244000,
        minChunks: 1,
        maxAsyncRequests: 30,
        maxInitialRequests: 30,
        cacheGroups: {
          defaultVendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            reuseExistingChunk: true,
          },
          default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true,
          },
        },
      };
    }

    return config;
  },
};

module.exports = withBundleAnalyzer(nextConfig);

写在最后

Next.js 14 提供了丰富的性能优化工具和特性。在实际应用中,需要注意以下几点:

  1. 合理使用图片和字体优化
  2. 实施有效的代码分割策略
  3. 优化数据缓存和预加载
  4. 监控和优化 Core Web Vitals
  5. 持续进行构建优化

在下一篇文章中,我们将深入探讨 Next.js 14 的部署与运维策略。如果你有任何问题或建议,欢迎在评论区讨论!

如果觉得这篇文章对你有帮助,别忘了点个赞 👍

相关推荐
量子-Alex10 分钟前
【目标检测】【PANet】Path Aggregation Network for Instance Segmentation
人工智能·目标检测·计算机视觉
lihuayong12 分钟前
计算机视觉:经典数据格式(VOC、YOLO、COCO)解析与转换(附代码)
人工智能·yolo·目标检测·计算机视觉·目标跟踪·coco·数据标注
thinkMoreAndDoMore18 分钟前
深度学习(3)-TensorFlow入门(常数张量和变量)
开发语言·人工智能·python
神舟之光19 分钟前
动手学深度学习2025.2.23-预备知识之-线性代数
人工智能·深度学习·线性代数
GISer_Jing22 分钟前
Node.js中如何修改全局变量的几种方式
前端·javascript·node.js
wapicn9931 分钟前
‌挖数据平台对接DeepSeek推出一键云端部署功能:API接口驱动金融、汽车等行业智能化升级
java·人工智能·python·金融·汽车·php
秋意钟39 分钟前
Element UI日期选择器默认显示1970年解决方案
前端·javascript·vue.js·elementui
不爱学习的YY酱39 分钟前
MusicGPT的本地化部署与远程调用:让你的Windows电脑成为AI音乐工作站
人工智能·windows
kakaZhui41 分钟前
【多模态大模型】端侧语音大模型minicpm-o:手机上的 GPT-4o 级多模态大模型
人工智能·chatgpt·aigc·llama
艾思科蓝 AiScholar1 小时前
【SPIE出版,见刊快速,EI检索稳定,浙江水利水电学院主办】2025年物理学与量子计算国际学术会议(ICPQC 2025)
图像处理·人工智能·信息可视化·自然语言处理·数据分析·力扣·量子计算