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

Next.js如何正确应用全局脚本 解决滚动淡入动画仅首屏生效问题

问题根因
  • 你的appearOnScroll.js是立即执行函数(IIFE),仅在页面首次加载时执行1次,Next.js走客户端路由切换页面不会触发全页重载,脚本不会重新执行
  • 脚本执行时只会查询首次渲染的.fade-in类元素,页面切换后新渲染的元素不会被加入观测队列
  • 你对进入视口的元素调用了unobserve取消观测,路由返回后原元素的appear类会随组件销毁重置,但不会被再次观测触发动画
  • 补充:现有脚本存在笔误,观测元素的方法应为observe(),你写的observer()是实例对象名,调用会报错
更优实现方案(推荐)

放弃全局脚本的实现方式,改用React组件化方式实现,完全适配Next.js客户端路由逻辑,无内存泄漏问题:

  1. 先实现通用的淡入包装组件
// components/FadeInItem.js
import { useRef, useEffect } from 'react'

export default function FadeInItem({ children, threshold = 1 }) {
  const elRef = useRef(null)
  
  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('appear')
          observer.unobserve(entry.target)
        }
      })
    }, { threshold })
    
    if (elRef.current) {
      observer.observe(elRef.current)
    }
    
    // 组件卸载时自动取消观测,避免内存泄漏
    return () => {
      if (elRef.current) {
        observer.unobserve(elRef.current)
      }
    }
  }, [threshold])

  return <div ref={elRef} className="fade-in">{children}</div>
}
  1. 替换现有所有直接加fade-in类的元素,用FadeInItem包裹即可:
// 原来的写法
<div className="fade-in">内容</div>
// 替换为
<FadeInItem>内容</FadeInItem>
  1. 保留原有CSS即可:
.fade-in {
  opacity: 0;
  transition: opacity 0.4s ease-in-out;
}
.fade-in.appear {
  opacity: 1;
}
现有代码快速修复方案(不改动现有类名绑定)

如果不想批量修改现有元素绑定,可直接修改FadeIn组件,监听Next.js路由变化每次重新执行观测逻辑:

  1. 删掉appearOnScroll.js外部脚本,把逻辑移到FadeIn组件内部:
// components/FadeIn.js
import { useEffect } from 'react'
import { useRouter } from 'next/router'

export default function FadeIn() {
  const router = useRouter()

  const initFadeObserver = () => {
    const faders = document.querySelectorAll('.fade-in');
    const faderOptions = {
      threshold: 1
    };
    const appearOnScroll = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (!entry.isIntersecting) return
        entry.target.classList.add('appear')
        observer.unobserve(entry.target)
      })
    }, faderOptions)
    // 修正原脚本的方法名笔误
    faders.forEach(fader => appearOnScroll.observe(fader))
  }

  useEffect(() => {
    // 首次加载执行
    initFadeObserver()
    // 每次路由切换完成后重新执行
    router.events.on('routeChangeComplete', initFadeObserver)
    // 组件卸载移除监听
    return () => {
      router.events.off('routeChangeComplete', initFadeObserver)
    }
  }, [router.events])

  return null
}
  1. 删掉原来的Script引入和Portal逻辑,直接在_app.js里保留<FadeIn />引入即可。

内容的提问来源于stack exchange,提问作者dylzee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 19:36:01