Next.js 应用性能优化实战:从 CSR 到 SSG 的演进过程

在最近的项目实践中,我遇到了一个首页性能优化的问题。首页包含一个较为复杂的 AnimatedBanner 组件,具有大量的图片和鼠标跟随动画。最初的实现方式为客户端渲染(CSR),带来了较大的首屏加载压力。本文将详细记录我如何逐步优化 AnimatedBanner,从客户端渲染 (CSR) 到静态站点生成 (SSG),并解决了在过程中遇到的各种问题。

一、初始问题分析

最开始,我的 AnimatedBanner 组件采用 use client,通过 useEffect 直接加载动画逻辑。虽然功能完整,但带来了几个问题:

  • 首屏加载 JS 量大,影响渲染速度。
  • 组件树使用 use client 后,无法利用 Next.js 的静态生成优势(SSG)。

二、渐进式优化策略

我采用了一步一步的优化策略:

  1. 动态加载动画逻辑
  2. 将组件拆分为 Server 和 Client Component
  3. 使用 IntersectionObserver 懒加载动画
  4. 引入 ``** 组件懒加载**
  5. 使用 ``** 提升图片加载体验**

三、优化实施过程

1. 动态加载动画逻辑

原本动画逻辑通过 useEffect 直接导入,我将其修改为动态导入方式,减小初始加载体积:

tsx 复制代码
useEffect(() => {
  import("@/utils/bannerMouseEffect").then(({ initBannerMouseEffect }) => {
    initBannerMouseEffect();
  });
}, []);

2. 拆分 Server 和 Client Component

为了充分利用 Next.js 的静态生成,我将 AnimatedBanner 拆分为纯 Server Component 和 Client Component Wrapper。

  • Server Component(AnimatedBanner):只负责静态 HTML 和图片渲染。
  • Client Component(AnimatedBannerClient):负责加载和执行动画逻辑。

Server Component示例:

tsx 复制代码
const AnimatedBanner = forwardRef(({ style }, ref) => (
  <div ref={ref} className="animated-banner">
    {/* 图片渲染 */}
  </div>
));
AnimatedBanner.displayName = "AnimatedBanner";

Client Component示例:

tsx 复制代码
"use client";
​
const AnimatedBannerClient = ({ style }) => {
  const bannerRef = useRef(null);
​
  useEffect(() => {
    if ("IntersectionObserver" in window) {
      const observer = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting) {
          import("@/utils/bannerMouseEffect").then(({ initBannerMouseEffect }) => {
            initBannerMouseEffect();
          });
          observer.disconnect();
        }
      });
      observer.observe(bannerRef.current);
    }
  }, []);
​
  return <AnimatedBanner ref={bannerRef} style={style} />;
};

3. 使用 IntersectionObserver 懒加载动画

使用 IntersectionObserver 实现只有当用户滚动到组件时才加载动画逻辑,有效提升性能。

4. 使用 next/dynamic 组件懒加载

通过 Next.js 内置的动态导入进一步优化:

tsx 复制代码
const AnimatedBannerClient = dynamic(() => import("@/components/AnimatedBannerClient"), {
  ssr: false,
  loading: () => <div>加载中...</div>,
});

5. 提升图片加载体验:blurDataURL

为图片添加低分辨率占位图,改善加载体验:

tsx 复制代码
<Image
  src={item.src}
  blurDataURL={item.blurDataURL}
  placeholder="blur"
/>

四、遇到的问题与解决方案

优化过程中,遇到以下问题:

  • 父组件使用 "use client" 后,子组件无法静态渲染问题

    解决方案:拆分组件,确保 Banner 部分为纯 Server Component,通过 Client Wrapper 加载动画。

  • forwardRef 组件 ESLint 警告问题

    添加 displayName 解决警告:

    tsx 复制代码
    AnimatedBanner.displayName = "AnimatedBanner";
  • 图片未显示问题

    确保 ref 正确透传,并去除不必要的 ssr: false 配置即可。

五、最终效果及结论

通过以上优化措施,首页性能大幅提升:

  • 首屏秒开:静态 HTML 直接加载,图片显示平滑。
  • 动态动画延迟加载:动画逻辑按需加载,减少用户初次访问的等待时间。
  • 维护性提升:组件结构清晰,后续维护更加方便。

此次优化实践证明了 Next.js 应用合理使用 Server 和 Client Components 的重要性。通过精细化控制渲染模式,完全可以实现更好的用户体验和性能。

希望本次实践能够为遇到类似问题的开发者提供一些参考和思路。

相关推荐
lens941 天前
RSC、SSR傻傻分不清?一文搞懂所有渲染概念!
前端·next.js
求知摆渡1 天前
Next.js 渲染模式深度解析与SEO优化
next.js
玲小珑4 天前
Next.js 教程系列(十六)Next.js 中的状态管理方案
前端·next.js
玲小珑5 天前
Next.js 教程系列(十五)复杂表单处理与数据校验
前端·next.js
玲小珑8 天前
Next.js 教程系列(十四)NextAuth.js 身份认证与授权
前端·next.js
玲小珑9 天前
Next.js 教程系列(十三)Server Actions:新一代全栈能力
前端·next.js
droidHZ10 天前
第一次赚美元!纯新手深度复盘网站出海,一文掌握全流程
前端·ai编程·next.js
玲小珑10 天前
Next.js 教程系列(十二)API Routes:构建轻量级后端服务
前端·next.js
玲小珑11 天前
Next.js 教程系列(十一)数据缓存策略与 Next.js 运行时
前端·next.js