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 的重要性。通过精细化控制渲染模式,完全可以实现更好的用户体验和性能。

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

相关推荐
名字还没想好☜1 天前
Next.js ‘use client‘ 到底加在哪:Server/Client Components 边界与常见报错
开发语言·前端·javascript·react·next.js
濮水大叔2 天前
为什么 AI 最擅长 React/Next.js,却很少看到真正好用的 Next.js 开源项目?
react.js·node.js·next.js
倾颜3 天前
AI 生图不只是调 API,一条生产级生图链路的设计与工程取舍
agent·next.js
倾颜3 天前
多智能体(Agent)不靠运气 ,一个受控协作系统的设计复盘
agent·next.js
名字还没想好☜5 天前
React 受控输入框光标跳到末尾:格式化输入时的 selection 丢失 bug 与修复
前端·javascript·react.js·bug·react·next.js
名字还没想好☜6 天前
Next.js App Router 约定式文件实战:loading、error、not-found 怎么兜住加载态与异常
开发语言·前端·javascript·react·next.js
水獭比特8 天前
SSR 不等于性能优化:同一页面三种渲染路径实测
next.js
Zacks_xdc9 天前
【从0开发一个 Agent】第十七章:完整项目回顾与架构终章
人工智能·架构·agent·next.js
Damai11 天前
流式文件响应 `Controller is already closed` 崩溃复盘
next.js
人间凡尔赛11 天前
React Compiler + Server Components 实战:2026 前端全栈开发新范式
typescript·react·next.js·全栈开发·react compiler·server components