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

Next.js 15单页应用:点击导航栏li时匹配哈希URL添加active类

Next.js 15 哈希路由导航栏active类解决方案

问题原因

直接通过router.asPath === "/#services"判断不生效,核心原因有两点:

  1. 哈希变化属于客户端状态,静态组件不会自动触发重新渲染
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:45:13