如何在Next.js中实现带路由哈希的平滑滚动?
解决Next.js中哈希路由结合平滑滚动的问题
核心思路
要同时实现地址栏显示哈希链接和平滑滚动,需要手动控制路由哈希更新,同时避免Next.js默认的即时滚动行为,具体方案如下:
步骤1:优化平滑滚动函数
替换原有的scrollTo函数,使用更简洁可靠的scrollIntoView API:
export const scrollTo = (id: string) => { const element = document.getElementById(id); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'start' }); } };
步骤2:修改Navigation组件逻辑
在点击导航时,手动更新路由哈希(不触发页面重渲染),同时执行平滑滚动,还要监听路由哈希变化以覆盖初始加载、浏览器前进后退等场景:
import Link from 'next/link'; import React, { useEffect } from 'react'; import _ from 'lodash'; import useAppRouter from '../../../hooks/useAppRouter'; import { scrollTo } from '../path-to-scrollTo'; // 替换为实际文件路径 interface INavigation { links: string[]; } const Navigation: React.FC<INavigation> = ({ links }) => { const router = useAppRouter(); const handleClick = (e: React.MouseEvent<HTMLAnchorElement>, link: string) => { e.preventDefault(); // 用shallow模式更新哈希,不触发页面重渲染 router.push(`#${link}`, undefined, { shallow: true }); // 执行平滑滚动 scrollTo(link); }; // 监听哈希变化,处理初始加载、路由切换场景 useEffect(() => { if (router.hash) { const targetId = router.hash.slice(1); scrollTo(targetId); } }, [router.hash]); return ( <section className="stories-navigation"> <ul> {links.map((link: string) => ( <li key={link}> <Link href={`#${link}`}> <a onClick={(e) => handleClick(e, link)} className={router.hash === `#${link}` ? 'active-link' : ''} > {_.capitalize(link)} </a> </Link> </li> ))} </ul> </section> ); }; export default Navigation;
关键细节说明
- shallow路由更新:使用
router.push的shallow: true参数,仅更新地址栏哈希,不会触发页面重新渲染,保证性能。 - 哈希匹配修正:
router.hash返回的是带#的字符串(如#about),因此激活状态判断需改为router.hash ===#${link}``。 - 全局哈希监听:通过
useEffect监听router.hash变化,覆盖所有哈希变更场景(包括手动修改地址栏、浏览器前进后退)。
内容的提问来源于stack exchange,提问作者Yeva Terteryan
相关产品推荐
相关产品推荐

