我不知道的 React-Router:Link、NavLink 与 useNavigate 导航解析
571 字 10 min read
ReactReact-RouterLinkNavLinkuseNavigateSPAHistory API
在单页应用 (SPA) 中,流畅的页面切换体验至关重要。React Router 提供了几种关键工具来实现这一目标,其中最常用的就是 Link 组件、NavLink 组件以及 useNavigate Hook。
Link 组件:客户端导航的基础
Link 组件是 React Router 中实现声明式导航的核心。它看起来像一个普通的 <a> 标签,但行为却大不相同。
Link 与 <a> 标签的核心区别
<a>标签 (原生 HTML):\n * 点击<a>标签会触发浏览器向服务器发送一个新的 HTTP 请求,请求href指定的 URL。\n * 服务器响应后,浏览器会完全重新加载整个页面,即使目标页面和当前页面共享很多资源。\n * 这会导致页面闪烁、状态丢失,破坏了 SPA 的流畅体验。\n * 示例:<a href="/about">关于我们</a>\n<Link>组件 (React Router):\n *Link组件渲染出来也是一个<a>标签,但它添加了额外的逻辑。\n * 阻止默认行为: 它会监听点击事件,并调用event.preventDefault()来阻止浏览器执行默认的页面跳转和刷新。\n * 更新 URL: 接着,它会使用 History API (通常是history.pushState()) 来在浏览器历史记录中添加新的条目并更新地址栏的 URL,但不会向服务器发送请求。\n * 触发 React 更新: URL 的变化会被 React Router 监听(通常由BrowserRouter或HashRouter实现),进而通知相关的<Route>组件根据新的 URL 渲染对应的视图内容。React 会高效地更新 DOM,只改变需要变化的部分。\n * 这样就实现了在不刷新整个页面的情况下切换视图,保持了应用的单页特性。\n * 示例:<Link to="/about">关于我们</Link>\n
Link 的内部工作流程 (简化版)
- 用户点击
<Link to="/target">组件渲染出的<a>标签。 Link组件内部的onClick处理函数被触发。- 处理函数调用
event.preventDefault()。 - 处理函数获取
toprop 指定的目标路径 (/target)。 - 处理函数调用 React Router 的
history对象的方法(如history.push('/target'))。 history.push内部调用浏览器的window.history.pushState({}, '', '/target')来更新 URL 和浏览器历史记录。- React Router 的路由监听器(如
BrowserRouter内部的popstate监听器或history库的listen方法)检测到 URL 变化。 - React Router 根据新的 URL 匹配对应的
<Route>。 - 匹配到的
<Route>及其关联的组件被渲染,React 更新 DOM。
NavLink 组件:添加激活状态样式
NavLink 是 Link 的一个特殊版本,它知道当前链接是否"激活"(即其 to prop 是否匹配当前的 URL),并允许你方便地为激活状态添加样式。
如何判断激活状态?
NavLink 默认在以下情况被认为是激活的:
- 当前 URL 完全匹配
toprop 的路径 - 如果设置了
endprop (boolean),则只有在完全匹配时才激活(忽略后续的/或子路径)。默认end={false},表示/users的NavLink在/users/123路径下也会被视为激活(部分匹配)。
添加激活样式的方式
activeClassName(v5 及更早版本,v6 中已废弃): 传递一个 CSS 类名,当链接激活时会自动添加到<a>标签上。activeStyle(v5 及更早版本,v6 中已废弃): 传递一个内联样式对象,当链接激活时会自动应用。- 函数作为
className或styleprop (v6+ 推荐): 这是 v6 中最灵活的方式。你可以给className或styleprop 传递一个函数,该函数接收一个包含isActive布尔值的对象作为参数,你可以根据isActive的值动态返回类名或样式。
import { NavLink } from 'react-router-dom';
function Navigation() {
return (
<nav>
{/* 使用函数 className */}
<NavLink
to="/messages"
className={({ isActive }) =>
isActive ? 'active-link' : 'inactive-link'
}
>
消息
</NavLink>
{/* 使用函数 style */}
<NavLink
to="/tasks"
style={({ isActive }) => ({
color: isActive ? 'red' : 'black',
fontWeight: isActive ? 'bold' : 'normal'
})}
>
任务
</NavLink>
{/* 使用 end prop 精确匹配 */}
<NavLink to="/home" end className={({isActive}) => isActive ? 'active' : ''}>
首页
</NavLink>
</nav>
);
}
useNavigate Hook:编程式导航
Link 和 NavLink 适用于用户点击触发的导航。但有时我们需要在代码逻辑中(例如,表单提交成功后、用户执行某个操作后)进行页面跳转,这时就需要编程式导航。
useNavigate Hook 提供了执行编程式导航的能力。
如何使用?
- 在你的函数组件中调用
useNavigate()Hook,它会返回一个navigate函数。 - 调用
navigate(to, options)函数来执行跳转。
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const handleSubmit = async (event) => {
event.preventDefault();
// 假设 loginUser 是一个异步登录函数
const loginSuccess = await loginUser(/* ... */);
if (loginSuccess) {
// 登录成功后跳转到仪表盘
navigate('/dashboard');
} else {
// 显示错误信息...
}
};
return (
<form onSubmit={handleSubmit}>
{/* ...表单元素... */}
<button type="submit">登录</button>
</form>
);
}
navigate 函数的参数
to(string): 目标路径,与Link的toprop 类似。to(number): 可以传入一个数字,如navigate(-1)来模拟浏览器的"后退"按钮,navigate(1)模拟"前进",navigate(-2)后退两步等。options(object): 可选配置对象:replace: true: 如果设置为true,则导航会替换历史记录中的当前条目,而不是添加新条目。这意味着用户点击浏览器后退按钮时,不会回到执行navigate之前的页面。常用于登录成功后的跳转,避免用户回退到登录页。state: any: 允许你将一些状态数据附加到新的历史记录条目上。这个状态数据不会显示在 URL 中,但可以在目标路由组件中通过useLocation().state访问。适用于传递临时数据,如"来自哪个页面"的信息,或者一些需要在跳转后恢复的 UI 状态。
function ProductDetail({ product }) {
const navigate = useNavigate();
const handleAddToCart = () => {
// ... 添加到购物车的逻辑 ...
// 跳转到购物车页面,并替换当前历史记录,
// 同时传递一个状态表明是从哪个产品添加的
navigate('/cart', {
replace: true,
state: { addedProductId: product.id, message: '成功添加到购物车!' }
});
};
return <button onClick={handleAddToCart}>添加到购物车</button>;
}
// 在 /cart 页面对应的组件中:
import { useLocation } from 'react-router-dom';
function CartPage() {
const location = useLocation();
const message = location.state?.message; // 读取 navigate 传递过来的状态
return (
<div>
{message && <p style={{color: 'green'}}>{message}</p>}
{/* ... 购物车内容 ... */}
</div>
);
}
Link 与 state Prop
Link 组件也支持 state prop,功能与 navigate 的 state 选项完全相同,用于在声明式导航中传递路由状态。
<Link to="/profile" state={{ from: '/settings' }}>
前往个人资料
</Link>
// 在 /profile 页面组件中
function ProfilePage() {
const location = useLocation();
const fromPage = location.state?.from; // 获取 'settings'
// ...
}
总结
Link: 用于用户点击触发的基础客户端导航,避免页面刷新,通过to指定目标。NavLink:Link的增强版,能感知激活状态,并通过className或style的函数 prop 应用激活样式。useNavigate: 用于在 JS 逻辑中执行编程式导航,支持replace和state选项,也能实现历史记录的前进后退。
理解这三者的区别和用法,是构建流畅、交互自然的 React SPA 的关键。它们共同利用了 History API 和 React 的渲染机制,提供了比传统多页应用更好的用户体验。