我不知道的 React-Router:Link、NavLink 与 useNavigate 导航解析
571 字
10 min read
React React-Router Link NavLink useNavigate SPA History 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()
。 - 处理函数获取
to
prop 指定的目标路径 (/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 完全匹配
to
prop 的路径 - 如果设置了
end
prop (boolean),则只有在完全匹配时才激活(忽略后续的/
或子路径)。默认end={false}
,表示/users
的NavLink
在/users/123
路径下也会被视为激活(部分匹配)。
添加激活样式的方式
activeClassName
(v5 及更早版本,v6 中已废弃): 传递一个 CSS 类名,当链接激活时会自动添加到<a>
标签上。activeStyle
(v5 及更早版本,v6 中已废弃): 传递一个内联样式对象,当链接激活时会自动应用。- 函数作为
className
或style
prop (v6+ 推荐): 这是 v6 中最灵活的方式。你可以给className
或style
prop 传递一个函数,该函数接收一个包含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
的to
prop 类似。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 的渲染机制,提供了比传统多页应用更好的用户体验。