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

如何在ReactJS中结合React-Router-DOM维护URL导航与单页滚动链接

问题描述
  • 基于ReactJS + React-Router-DOM开发项目,导航栏包含home、about、services、portfolio、contact五个链接
  • home和about为单页滚动锚点链接,services/portfolio/contact对应独立路由页面
  • 使用<a>标签实现锚点跳转而非React-Router的Link组件,因Link对锚点的表现不符合预期
  • 正常场景:从home跳转到about时,URL从domain变为domain/#about;从domain/#about跳转到services时,URL变为domain/services
  • 异常场景:处于services/portfolio/contact页面时,点击home或about链接,URL会变为domain/services#about,而非预期的domain/#about
现有Navbar组件代码
import React, { useEffect } from "react";
import { Link, useLocation } from "react-router-dom";
import "./css/navbar.css";
import Logo from "./Logo";

function Navbar(props) {
  const location = useLocation();

  useEffect(() => {
    console.log(location);
  }, [location]);

  const singlePageScrollLinks = () => {
    return (
      <>
        <li className="nav-item">
          <a
            href="#home"
            className={`nav-link ${
              location.pathname === "/" && location.hash === "#home"
                ? "active"
                : ""
            }`}
          >
            Home
          </a>
        </li>
        <li className="nav-item">
          <a
            href="#about"
            className={`nav-link ${
              location.pathname === "/" && location.hash === "#about"
                ? "active"
                : ""
            }`}
          >
            About
          </a>
        </li>
      </>
    );
  };

  return (
    <div
      style={{
        position: "sticky",
        top: "-2px",
        backgroundColor: "#2c2c2c",
        zIndex: "2",
      }}
      id="nav-page"
    >
      <div className="d-flex justify-content-between list-unstyled top-nav">
        <li>+91 9876543210</li>
        <li>qwerty@mail.com</li>
      </div>
      <nav
        className="navbar navbar-expand-lg bg-body-tertiary custom-nav"
        style={{ borderRadius: "45px 0 0 0" }}
      >
        <div className="container-fluid">
          <Link className="navbar-brand" to="/" style={{ width: "1px" }}>
            <Logo />
          </Link>
          <button
            className="navbar-toggler"
            type="button"
            data-bs-toggle="collapse"
            data-bs-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent"
            aria-expanded="false"
            aria-label="Toggle navigation"
          >
            <span className="navbar-toggler-icon"></span>
          </button>
          <div className="collapse navbar-collapse" id="navbarSupportedContent">
            <ul className="navbar-nav ms-auto mb-2 mb-lg-0">
              {singlePageScrollLinks()}
              <li className="nav-item">
                <Link className="nav-link" to="/services">
                  Services
                </Link>
              </li>
              <li className="nav-item">
                <Link className="nav-link" to="/portfolio">
                  Portfolio
                </Link>
              </li>
              <li className="nav-item">
                <Link className="nav-link" to="/contact">
                  Contact
                </Link>
              </li>
            </ul>
          </div>
        </div>
      </nav>
    </div>
  );
}

export default Navbar;
解决方案

问题根源:相对路径的锚点href="#about"会在当前路由路径后追加哈希值,而非回到根路径。以下两种方式可修复:

方式1:使用绝对路径锚点

直接将<a>标签的href改为绝对路径,确保无论当前在哪个路由页面,点击都会跳转到根路径下的对应锚点:

// 修改singlePageScrollLinks里的a标签
<li className="nav-item">
  <a
    href="/#home"
    className={`nav-link ${
      location.pathname === "/" && location.hash === "#home"
        ? "active"
        : ""
    }`}
  >
    Home
  </a>
</li>
<li className="nav-item">
  <a
    href="/#about"
    className={`nav-link ${
      location.pathname === "/" && location.hash === "#about"
        ? "active"
        : ""
    }`}
  >
    About
  </a>
</li>

方式2:结合React-Router编程式导航(推荐)

若希望保留单页应用无刷新特性,可通过useNavigate实现跳转,先回到根路径再滚动到锚点:

  1. 导入useNavigate并初始化
  2. 为锚点链接添加点击事件,阻止默认跳转后手动导航到根路径+锚点
import React, { useEffect } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom"; // 新增useNavigate
import "./css/navbar.css";
import Logo from "./Logo";

function Navbar(props) {
  const location = useLocation();
  const navigate = useNavigate(); // 初始化navigate

  useEffect(() => {
    console.log(location);
  }, [location]);

  const handleAnchorClick = (e, anchor) => {
    e.preventDefault();
    // 导航到根路径下的对应锚点
    navigate(`/#${anchor}`);
    // 若自动滚动不生效,手动触发平滑滚动
    setTimeout(() => {
      document.getElementById(anchor)?.scrollIntoView({ behavior: 'smooth' });
    }, 100);
  };

  const singlePageScrollLinks = () => {
    return (
      <>
        <li className="nav-item">
          <a
            href="#home"
            onClick={(e) => handleAnchorClick(e, 'home')}
            className={`nav-link ${
              location.pathname === "/" && location.hash === "#home"
                ? "active"
                : ""
            }`}
          >
            Home
          </a>
        </li>
        <li className="nav-item">
          <a
            href="#about"
            onClick={(e) => handleAnchorClick(e, 'about')}
            className={`nav-link ${
              location.pathname === "/" && location.hash === "#about"
                ? "active"
                : ""
            }`}
          >
            About
          </a>
        </li>
      </>
    );
  };

  // 其余代码保持不变
}

export default Navbar;

额外说明

两种方式下,原有的激活状态判断逻辑均无需修改,location对象会正确反映当前路径与哈希值,确保active类名正常生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 09:16:12