未用react-router-dom,单页网站如何获取哈希路由并更新导航激活状态?
不需要使用react-router-dom,两种原生实现方案如下
方案一:监听浏览器哈希变化(适合直接通过a标签哈希跳转的场景)
利用浏览器原生的hashchange事件,监听地址栏哈希值的变化,同步更新导航激活状态,同时初始化时读取当前哈希值避免页面刷新后状态不一致。
import { useState, useEffect } from 'react'; import { AiOutlineHome, AiOutlineUser } from 'react-icons/ai'; const Nav = ({ isToggled, onToggle }) => { // 初始化时读取当前地址栏哈希,默认值为# const [activeNav, setActiveNav] = useState(window.location.hash || '#'); useEffect(() => { const handleHashChange = () => { const currentHash = window.location.hash || '#'; setActiveNav(currentHash); }; // 挂载时绑定哈希变化监听 window.addEventListener('hashchange', handleHashChange); // 卸载时移除监听,避免内存泄漏 return () => window.removeEventListener('hashchange', handleHashChange); }, []); return ( <nav> <a href='#' onClick={() => setActiveNav('#')} // 点击时即时更新状态,提升交互流畅度 className={activeNav === '#' ? 'active' : ''} > <AiOutlineHome /> </a> <a href='#about' onClick={() => setActiveNav('#about')} className={activeNav === '#about' ? 'active' : ''} > <AiOutlineUser /> </a> {/* 其他导航项 */} </nav> ); };
方案二:监听滚动事件(适合平滑滚动到锚点的场景)
如果你的页面是通过滚动定位到对应区块(而非直接修改哈希),可以监听滚动事件,判断当前视口内的目标区块,同步更新导航激活状态。
import { useState, useEffect } from 'react'; import { AiOutlineHome, AiOutlineUser } from 'react-icons/ai'; const Nav = ({ isToggled, onToggle }) => { const [activeNav, setActiveNav] = useState('#'); useEffect(() => { // 节流函数,优化滚动事件触发频率 const throttle = (func, delay) => { let timer = null; return () => { if (!timer) { timer = setTimeout(() => { func(); timer = null; }, delay); } }; }; const handleScroll = () => { const sections = document.querySelectorAll('section[id]'); let currentHash = '#'; sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.offsetHeight; // 100是偏移量,可根据导航栏高度调整 if (window.scrollY >= sectionTop - 100 && window.scrollY < sectionTop + sectionHeight - 100) { currentHash = `#${section.id}`; } }); if (currentHash !== activeNav) { setActiveNav(currentHash); // 可选:同步更新地址栏哈希 window.history.pushState(null, null, currentHash); } }; const throttledScroll = throttle(handleScroll, 100); window.addEventListener('scroll', throttledScroll); // 初始化时执行一次,设置初始状态 handleScroll(); return () => window.removeEventListener('scroll', throttledScroll); }, [activeNav]); return ( <nav> <a href='#' onClick={(e) => { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); setActiveNav('#'); }} className={activeNav === '#' ? 'active' : ''} > <AiOutlineHome /> </a> <a href='#about' onClick={(e) => { e.preventDefault(); document.getElementById('about').scrollIntoView({ behavior: 'smooth' }); setActiveNav('#about'); }} className={activeNav === '#about' ? 'active' : ''} > <AiOutlineUser /> </a> {/* 其他导航项 */} </nav> ); };
方案选择建议
- 若只是单纯通过a标签哈希跳转,方案一足够简洁高效;
- 若使用平滑滚动到锚点的交互逻辑,方案二能更精准地匹配当前视口位置。
内容的提问来源于stack exchange,提问作者AfonsoSalgadoSousa
相关产品推荐
相关产品推荐

