NextJS跨页面跳转后如何实现锚点平滑滚动
NextJS跨页面锚点平滑滚动问题修复
现有代码的核心错误
- onClick绑定逻辑错误导致定时器提前执行:你直接将
setTimeout()的执行结果作为onClick的回调传入,而非传入包裹定时器的函数。这会导致组件渲染阶段setTimeout就会被立即注册,不需要用户点击就会在2秒后触发滚动逻辑,这就是页面未点击也会自动滚动到program区块的直接原因。 - 服务端渲染阶段非法访问浏览器API:NextJS默认会在服务端完成组件首屏渲染,服务端运行环境不存在浏览器专属的
document全局对象,提前触发的定时器在服务端执行时调用document相关方法,就会抛出document is not defined的引用错误。 - 固定延时等待页面加载的逻辑不可靠:写死2秒延迟完全依赖主观判断的加载时长,网络较差时2秒内页面DOM可能还未渲染完成,目标区块不存在会导致滚动失效;网络较好时用户需要空等2秒才会触发滚动,交互体验很差。
- 事件绑定冗余:外层导航项div和内部a标签重复绑定了点击逻辑,事件冒泡会导致回调重复触发。
正确实现方案
不需要依赖固定延时,利用NextJS路由事件配合原生锚点能力实现,逻辑更稳定:
1. 修改导航栏跳转逻辑
直接在跳转路径上拼接锚点参数,移除导航组件内的滚动、延时逻辑,避免提前执行问题:
<div className={styles.navItem}> <Link href="/" passHref> <a className={styles.navLinks} onClick={toggle}> Home </a> </Link> </div> <div className={styles.navItem}> <Link href="/#about" passHref> <a className={styles.navLinks} onClick={toggle}> About Us </a> </Link> </div> <div className={styles.navItem}> <Link href="/#program" passHref> <a className={styles.navLinks} onClick={toggle}> Program Overview </a> </Link> </div> <div className={styles.navItem}> <Link href="/leaderboard" passHref> <a className={styles.navLinks} onClick={toggle}> Leaderboard </a> </Link> </div>
2. 首页监听路由事件触发滚动
将所有浏览器API相关逻辑放到客户端执行的生命周期中,避免服务端渲染报错:
如果你用的是Pages Router(pages/目录结构)
在首页文件pages/index.js中添加路由监听逻辑,路由跳转完成、DOM渲染结束后再触发滚动:
import { useRouter } from 'next/router' import { useEffect } from 'react' export default function Home() { const router = useRouter() useEffect(() => { const scrollToHash = (url) => { const hash = url.split('#')[1] if (!hash) return // 延时0等当前帧DOM渲染完成即可,不需要等2秒 setTimeout(() => { const targetEl = document.getElementById(hash) targetEl?.scrollIntoView({ behavior: 'smooth', block: 'end' }) }, 0) } // 首次进入页面带锚点时触发滚动 if (router.asPath.includes('#')) { scrollToHash(router.asPath) } // 监听路由跳转完成事件 router.events.on('routeChangeComplete', scrollToHash) return () => { router.events.off('routeChangeComplete', scrollToHash) } }, [router]) return ( <main> {/* 首页其他内容 */} <section id="about">About Us 区块内容</section> <section id="program">Program Overview 区块内容</section> </main> ) }
如果你用的是App Router(app/目录结构)
不需要额外写JS监听逻辑,只要给对应区块设置匹配的id属性,再在全局CSS中添加平滑滚动配置即可:
html { scroll-behavior: smooth; }
注意:所有访问
document、window的浏览器端API,都要放到useEffect、事件回调这类仅在客户端执行的代码块中,从根源上避免服务端渲染报错。
内容的提问来源于stack exchange,提问作者Owenn
相关产品推荐
相关产品推荐

