Next.js 15单页应用:点击导航栏li时匹配哈希URL添加active类
Next.js 15 哈希路由导航栏active类解决方案
问题原因
直接通过router.asPath === "/#services"判断不生效,核心原因有两点:
- 哈希变化属于客户端状态,静态组件不会自动触发重新渲染
router.asPath的格式可能和预期不符(比如路径后附带其他参数时)
可行方案
方案一:监听window哈希变化(推荐)
通过原生window.location.hash和hashchange事件监听哈希变化,确保导航栏能实时更新active类,需将组件标记为客户端组件:
'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; export default function Navbar() { // 初始化当前哈希状态 const [activeHash, setActiveHash] = useState(window.location.hash); // 监听哈希变化并更新状态 useEffect(() => { const handleHashChange = () => setActiveHash(window.location.hash); window.addEventListener('hashchange', handleHashChange); return () => window.removeEventListener('hashchange', handleHashChange); }, []); // 封装类名判断逻辑 const getNavLinkClass = (targetHash) => { const baseClass = 'nav-link p-lg-3 p-4'; return activeHash === targetHash ? `${baseClass} active` : baseClass; }; return ( <ul className="navbar-nav ms-auto mb-2 mb-lg-0"> <li className="nav-item"> <Link className={getNavLinkClass('#')} href="/">Home</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#product')} href="/#product">Products</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#services')} href="/#services">Services</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#about_us')} href="/#about_us">About us</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#')} href="/#">Support</Link> </li> <li className="nav-item"> <Link className={`nav-link p-lg-3 p-4 ${window.location.pathname === '/account/login' ? 'active' : ''}`} href="/account/login" >Login</Link> </li> </ul> ); }
方案二:使用Next.js路由API
通过useRouter获取当前路径,提取哈希部分进行判断,同样需要标记为客户端组件:
'use client'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; export default function Navbar() { const router = useRouter(); // 从asPath中提取哈希部分 const currentHash = router.asPath.includes('#') ? `#${router.asPath.split('#')[1]}` : '#'; const getNavLinkClass = (targetHash) => { const baseClass = 'nav-link p-lg-3 p-4'; return currentHash === targetHash ? `${baseClass} active` : baseClass; }; return ( <ul className="navbar-nav ms-auto mb-2 mb-lg-0"> <li className="nav-item"> <Link className={getNavLinkClass('#')} href="/">Home</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#product')} href="/#product">Products</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#services')} href="/#services">Services</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#about_us')} href="/#about_us">About us</Link> </li> <li className="nav-item"> <Link className={getNavLinkClass('#')} href="/#">Support</Link> </li> <li className="nav-item"> <Link className={`nav-link p-lg-3 p-4 ${router.asPath === '/account/login' ? 'active' : ''}`} href="/account/login" >Login</Link> </li> </ul> ); }
注意事项
- 所有涉及客户端状态或路由监听的组件必须添加
'use client'指令 - 对于Login这类非哈希路由,直接判断
router.asPath或window.location.pathname即可 - 方案一更适合纯锚点导航场景,响应更直接;方案二更贴合Next.js生态
内容的提问来源于stack exchange,提问作者K.A
相关产品推荐
相关产品推荐

