You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js应用首次滚动卡顿 react-slick引发导航栏性能问题

问题说明

将原有React应用重构为Next.js项目后,滚动到指定位置触发下拉过渡效果的导航栏在页面首次加载时,滚动操作会出现数毫秒的卡顿冻结,导航栏下拉效果首次触发完成后功能恢复正常。该问题在纯React项目中未出现,经排查为react-slick轮播组件与导航栏滚动逻辑冲突导致。


根因分析
  • 导航栏重复绑定了2个scroll事件监听器,且未做性能优化,滚动时高频触发状态更新,和react-slick初始化时的尺寸计算、重排操作抢占主线程,直接导致首次滚动卡顿
  • 导航栏状态更新逻辑存在bug:滚动高度超过112px时执行setNewNav(!newNav),滚动过程中会反复切换导航栏显示状态,触发大量无意义重渲染
  • Carousel组件在渲染函数主流程中直接调用父组件传入的状态更新回调,每次组件重渲染都会触发父组件重渲染,和滚动事件的状态更新叠加形成渲染阻塞
  • Next.js服务端渲染的特性下,react-slick在客户端hydration阶段需要重新计算所有轮播项布局,初始化开销远大于纯客户端React项目,和首屏滚动逻辑叠加就会出现首次冻结

修复方案
  1. 合并重复的scroll监听,添加节流,标记监听为passive提升滚动性能,修正状态更新逻辑,仅在滚动阈值跨越时触发导航栏状态切换
  2. 修复Carousel组件的反模式调用,将父组件回调移入副作用钩子,禁止在渲染主流程中直接触发父组件状态更新
  3. 对轮播组件使用Next.js动态导入,关闭服务端渲染,延后组件初始化时机,避免和首屏导航栏交互抢占主线程
  4. 优化react-slick配置,使用按需懒加载减少初始化开销,给轮播图片添加原生懒加载属性

修正后代码示例

导航栏组件核心逻辑修正

const NavComponent = () => {
  const isDesktopOrLaptop = useMediaQuery({ query: '(min-width: 1224px)' })
  const isMobile = useMediaQuery({ query: '(max-width: 768px)' })

  const [open, setOpen] = useState(false);
  const [scroll, setScroll] = useState(0);
  const [newNav, setNewNav] = useState(false)
  const [show, setShow] = useState(false);

  // 滚动节流,对齐浏览器16ms帧刷新间隔
  const throttle = (func, delay = 16) => {
    let timer = null;
    return (...args) => {
      if (timer) return;
      timer = setTimeout(() => {
        func.apply(this, args);
        timer = null;
      }, delay)
    }
  }

  React.useEffect(() => {
    const handleScroll = throttle(() => {
      if (window === undefined) return;
      const scrollY = window.scrollY;
      // 合并两个滚动监听逻辑,减少重复触发
      setScroll(scrollY > 112 ? scrollY : 0);
      // 修正原逻辑中反复取反的bug,仅在状态变化时更新
      setNewNav(scrollY > 112);
    })
    // 标记passive告诉浏览器监听不会阻止默认滚动行为,大幅提升滚动性能
    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  const navClass = newNav ? "menu2_show" : "menu2"
  const handleClose = () => setShow(prev => !prev);
  const handleShow = () => setShow(prev => !prev);

  // 其余JSX结构保持原有逻辑即可,给下拉导航栏元素添加CSS属性 will-change: transform 提示浏览器提前创建合成层,减少重排开销
}

轮播组件修正

import React from "react"; 
import { useState, useEffect } from "react";
import Slider from "react-slick";
import Link from "next/link"

function Carousel(props) {
    const [zoomed, setZoomed] = useState('backgroundSize:550')
    const [fading_3, setFading_3] = useState(false)
    const [indexValue, setIndexValue] = useState({})
    const [imageIndex, setImageIndex] = useState(0);

    useEffect(() => {
      // 修正原逻辑中不必要的取反操作
      setFading_3(true)
    }, [])

    useEffect(() => {
      const currentVal = props.array[imageIndex];
      setIndexValue(currentVal);
      // 将父组件回调移入副作用,仅在轮播索引变化时触发
      props.sendDataToParent(currentVal)
    }, [imageIndex, props.array, props.sendDataToParent])

    const classes_4 = fading_3 ? 'zoomClass' : 'zoomClass hide'
    const settings = {
      focusOnSelect: true,
      infinite: true, 
      lazyLoad: 'ondemand', // 替换原布尔值配置,按需加载图片降低初始化开销
      speed: 600,
      slidesToShow: 3,
      centerMode: true,
      centerPadding: 0,
      beforeChange: (current, next) => setImageIndex(next),
    };

    return (
      <div className="App">
          <Slider {...settings} className={classes_4}>
              {props.array.map((img, idx) => ( 
              <div key={img._id} className={idx === imageIndex ? "slide activeSlide" : "slide"}>
                  {idx === imageIndex ?
                      <Link href="/">
                          <img loading="lazy" src={img.img} alt={img.alt || ''}  />
                      </Link> :
                      <img loading="lazy" src={img.img} alt={img.alt || ''}  />
                  }
              </div>
              ))}
          </Slider> 
      </div>
    );
}

export default Carousel;

页面引入轮播组件方式修正

import dynamic from 'next/dynamic';
// 动态导入轮播组件,关闭服务端渲染,延后初始化时机避免抢占首屏主线程
const Carousel = dynamic(() => import('./Carousel'), {
  ssr: false,
  loading: () => <div className="carousel-placeholder"></div>
})

内容的提问来源于stack exchange,提问作者József Kiss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 15:03:24