在单页应用(SPA)中,页面切换不会触发完整的浏览器刷新,而是通过 JavaScript 动态替换页面内容。React Router v6 是目前 React 生态里最主流的路由解决方案,提供了声明式、可组合的 API,并且完整支持嵌套路由、相对路径、数据加载等现代特性。
安装与起步
npm install react-router-dom
核心组件全部从 react-router-dom 导入:
import { BrowserRouter, Routes, Route, Link, Outlet } from 'react-router-dom';
最简路由配置
任何一个 React Router 应用都需要一个路由器组件包裹整个应用。BrowserRouter 是最常用的路由器,它使用 HTML5 的 history API 来保持 UI 和 URL 的同步。
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function Home() { return <h2>首页</h2>; }
function About() { return <h2>关于我们</h2>; }
function NotFound() { return <h2>404 页面不存在</h2>; }
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
要点说明:
BrowserRouter包裹在应用最外层,整个应用内部都可以使用路由。Routes用来定义路由规则,它会遍历内部的Route并返回第一个匹配的组件。Route通过path指定 URL 路径(区分大小写),通过element指定渲染的组件。path="*"表示匹配所有未被上面规则匹配的路径,通常用于 404 页面。
使用 Link 导航
不要使用 <a> 标签跳转,因为它会触发浏览器整页刷新。React Router 提供了 Link 组件,它会渲染一个 <a> 标签,但通过 history API 实现无刷新跳转。
import { Link } from 'react-router-dom';
function NavBar() {
return (
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
);
}
还可以使用 NavLink,它会在匹配到当前路径时自动添加 active 类名,方便高亮当前导航。
嵌套路由
嵌套路由允许在页面内定义子路由,适用于布局嵌套场景,比如后台管理系统的侧边栏布局、多级菜单等。v6 的嵌套路由不需要再在子组件中写 <Routes> 和 <Route>,而是直接在父路由中声明子 Route,并通过 <Outlet> 指定子路由的渲染位置。
示例:包含仪表盘和设置子页面的用户管理
// 布局组件
function UserLayout() {
return (
<div>
<h2>用户管理</h2>
<nav>
<Link to="dashboard">仪表盘</Link>
<Link to="settings">设置</Link>
</nav>
{/* 子路由内容在这里渲染 */}
<Outlet />
</div>
);
}
// 子页面组件
function Dashboard() { return <p>仪表盘内容</p>; }
function Settings() { return <p>设置页面</p>; }
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
{/* 嵌套路由 */}
<Route path="users" element={<UserLayout />}>
<Route index element={<Dashboard />} /> {/* 默认子路由 */}
<Route path="dashboard" element={<Dashboard />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</BrowserRouter>
);
}
说明:
Route path="users"是父路由,它的element是UserLayout,里面用<Outlet />作为占位符。- 子
Route的path是相对于父路径的,即/users/dashboard和/users/settings。 index路由:当用户访问/users时,默认显示Dashboard组件,相当于子路由的“首页”。没有index时,访问父路径只会渲染UserLayout,Outlet位置为空。
嵌套路由的另一种书写方式:集中配置
如果你习惯将所有路由集中在一个地方,也可以写成无组件嵌套的形式(将子路由直接放在父路由内部)。上面的例子已经是集中式,实际项目中通常将所有路由抽取到一个配置文件或一个专用组件中。
相对链接
在嵌套路由中使用 Link 时,推荐使用相对路径(不以 / 开头),这样当父路由变动时,子导航不必修改:
// 在 UserLayout 中
<Link to="dashboard">仪表盘</Link> // 解析为 /users/dashboard
<Link to="settings">设置</Link> // 解析为 /users/settings
如果用 <Link to="/users/dashboard"> 也是可以的,但失去了灵活性。
路由匹配与精确匹配
React Router v6 默认采用精确前缀匹配:即路径 /users 会匹配 /users、/users/dashboard 等所有以 /users 开头的路径,但不会匹配 /user。在嵌套路由中,父路由匹配后,会继续在子路由中查找最精确的匹配项。
编程式导航
除了 Link,还可以使用 useNavigate 钩子进行编程式跳转:
import { useNavigate } from 'react-router-dom';
function SomeComponent() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/about')}>跳转到关于</button>
);
}
注意事项
- 路由器选择:
BrowserRouter需要服务端配置支持(将路径重定向到index.html),否则刷新会出现 404。如果服务端无法配置,可以改用HashRouter(URL 中使用#符号)。 - 路由顺序:v6 使用最佳匹配而非顺序匹配,所以不必将精确路由放在前面,但如果有多个匹配(例如
/users/:id和/users/new),推荐将具体路径放在前面清晰表达意图。 - Outlet:必须存在,否则子路由内容不会显示。通常父组件就是一个布局组件,专门用来包裹侧边栏、导航栏等公共部分。
- 路径命名:路径变量使用冒号前缀,如
:userId,通过useParams获取。
掌握了这些基础,你就能搭建绝大多数应用的路由骨架了。后续还会涉及动态路由、路由守卫、数据路由等进阶特性,但都是在这些基础上叠加的更高层抽象。