在 React 项目开发中,页面之间的跳转与导航管理是必不可少的环节,而 React Router 就是解决这一问题的得力助手。它能够帮助我们轻松构建具有复杂导航逻辑的单页应用(SPA)。接下来,我们就全面且详细地学习 React Router 的各项功能。
快速开始,体验 React Router 的魅力
想要快速体验 React Router,我们可以直接在项目的index.js文件中进行路由配置。首先,确保你已经安装了react-router-dom库。如果没有安装,可以通过以下命令进行安装:
npm install react-router-dom
安装完成后,在index.js文件中引入相关模块并进行配置:
import React from'react';
import ReactDOM from'react-dom/client';
import { BrowserRouter as Router, Routes, Route } from'react-router-dom';
import Home from './components/Home';
import About from './components/About';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
</React.StrictMode>
);
上述代码中,BrowserRouter(这里使用别名Router)用于启用基于 HTML5 历史 API 的路由模式。Routes组件用于包裹所有的Route组件,Route组件则定义了具体的路由规则,path属性指定路由路径,element属性指定该路径对应的渲染组件。这样,一个简单的 React Router 应用就搭建完成了,当访问根路径/时,会渲染Home组件,访问/about时,会渲染About组件。
抽象路由模块,让项目结构更规范
随着项目规模的扩大,将所有路由配置都放在index.js中会使代码变得臃肿混乱。此时,我们可以对路由模块进行抽象,使项目结构更加清晰规范。
我们创建一个Page包用于存放所有的页面组件,每个组件在Page包中都有一个独立的文件夹,文件夹内包含一个index.js文件用于编写组件代码。例如,Home组件的目录结构如下:
- Page
- Home
- index.js
Home/index.js文件内容示例:
import React from'react';
const Home = () => {
return (
<div>
<h1>这是首页</h1>
</div>
);
};
export default Home;
同时,创建一个router包,在router/index.js文件中单独进行路由配置:
import { Routes, Route } from'react-router-dom';
import Home from '../Page/Home/index';
import About from '../Page/About/index';
const RouterConfig = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
);
};
export default RouterConfig;
然后在index.js文件中引入并使用这个路由配置组件:
import React from'react';
import ReactDOM from'react-dom/client';
import { BrowserRouter as Router } from'react-router-dom';
import RouterConfig from './router/index';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Router>
<RouterConfig />
</Router>
</React.StrictMode>
);
这样的结构调整,使得路由配置和组件代码都更加清晰易维护,方便后续的开发和扩展。
路由导航跳转:声明式与编程式的双剑合璧
在 React Router 中,实现路由导航跳转主要有两种方式:声明式写法的Link和编程式写法的navigate。
Link:声明式跳转
Link组件是声明式导航的代表,它的使用方式和 HTML 中的<a>标签类似,通过指定to属性来设置跳转的目标路径。例如:
import React from'react';
import { Link } from'react-router-dom';
const NavBar = () => {
return (
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
);
};
export default NavBar;
在上述代码中,点击首页链接会跳转到根路径/,点击关于链接会跳转到/about路径。Link组件会自动处理导航的逻辑,并且会根据当前路径给对应的链接添加active类名,方便我们进行样式的定制,以突出显示当前激活的链接。
navigate:编程式跳转
navigate是编程式导航的方法,它通过 JavaScript 代码来控制路由跳转,使用起来更加灵活。通常在事件处理函数或者异步操作中使用。首先,需要从react-router-dom中引入useNavigate钩子函数:
import React from'react';
import { useNavigate } from'react-router-dom';
const ButtonComponent = () => {
const navigate = useNavigate();
const handleClick = () => {
navigate('/about');
};
return (
<button onClick={handleClick}>跳转到关于页面</button>
);
};
export default ButtonComponent;
在上述代码中,当按钮被点击时,handleClick函数会调用navigate方法,实现跳转到/about路径。navigate方法还支持传入其他参数,比如navigate(-1)可以实现返回上一页的功能,类似于浏览器的后退按钮。
导航跳转传参:searchParams 与 Params 的灵活运用
在路由跳转过程中,我们经常需要传递一些参数。React Router 提供了searchParams和Params两种常用的传参方式。
searchParams 传参
searchParams传参方式类似于 URL 中的查询字符串,参数以键值对的形式附加在路径后面。例如,我们要传递一个用户 ID 到详情页面:
在跳转时:
import React from'react';
import { useNavigate } from'react-router-dom';
const UserList = () => {
const navigate = useNavigate();
const handleViewUser = (userId) => {
navigate(`/user?userId=${userId}`);
};
return (
<ul>
<li>
<button onClick={() => handleViewUser(1)}>查看用户1</button>
</li>
<li>
<button onClick={() => handleViewUser(2)}>查看用户2</button>
</li>
</ul>
);
};
export default UserList;
在接收参数的组件中,可以通过useSearchParams钩子函数获取参数:
import React from'react';
import { useSearchParams } from'react-router-dom';
const UserDetail = () => {
const [searchParams] = useSearchParams();
const userId = searchParams.get('userId');
return (
<div>
<h1>用户详情页面</h1>
<p>用户ID:{userId}</p>
</div>
);
};
export default UserDetail;
searchParams传参方式的优点是参数会显示在 URL 中,方便分享和书签标记,但缺点是参数长度有限制,且不太适合传递敏感信息。
Params 传参
Params传参方式是通过在路由路径中定义参数占位符来传递参数。例如:
在路由配置中定义参数路径:
import { Routes, Route } from'react-router-dom';
import UserDetail from '../Page/UserDetail/index';
const RouterConfig = () => {
return (
<Routes>
<Route path="/user/:userId" element={<UserDetail />} />
</Routes>
);
};
export default RouterConfig;
在跳转时:
import React from'react';
import { useNavigate } from'react-router-dom';
const UserList = () => {
const navigate = useNavigate();
const handleViewUser = (userId) => {
navigate(`/user/${userId}`);
};
return (
<ul>
<li>
<button onClick={() => handleViewUser(1)}>查看用户1</button>
</li>
<li>
<button onClick={() => handleViewUser(2)}>查看用户2</button>
</li>
</ul>
);
};
export default UserList;
在接收参数的组件中,通过useParams钩子函数获取参数:
import React from'react';
import { useParams } from'react-router-dom';
const UserDetail = () => {
const { userId } = useParams();
return (
<div>
<h1>用户详情页面</h1>
<p>用户ID:{userId}</p>
</div>
);
};
export default UserDetail;
Params传参方式的参数同样会显示在 URL 中,适用于传递具有特定含义的标识性参数,而且相比searchParams,它在 URL 中的展示更加简洁直观。
嵌套路由配置:构建多层次的页面结构
在实际项目中,很多时候页面会有多层次的结构,这就需要用到嵌套路由。例如,我们有一个博客应用,在文章列表页面中点击某篇文章,会在当前页面的右侧显示文章详情,而不是跳转到一个全新的页面,这就可以通过嵌套路由来实现。
首先,在路由配置中定义嵌套路由:
import { Routes, Route } from'react-router-dom';
import BlogList from '../Page/BlogList/index';
import BlogDetail from '../Page/BlogDetail/index';
const RouterConfig = () => {
return (
<Routes>
<Route path="/blog" element={<BlogList />}>
<Route path=":blogId" element={<BlogDetail />} />
</Route>
</Routes>
);
};
export default RouterConfig;
在上述代码中,/blog路径对应BlogList组件,而在BlogList组件内部,还可以有子路由,当访问形如/blog/1这样的路径时,会在BlogList组件的基础上,渲染BlogDetail组件,其中1就是传递给BlogDetail组件的文章 ID 参数。
BlogList组件中需要使用Outlet组件来指定子路由的渲染位置:
import React from'react';
import { Outlet, Link } from'react-router-dom';
const BlogList = () => {
return (
<div>
<h1>博客文章列表</h1>
<ul>
<li>
<Link to="/blog/1">文章1</Link>
</li>
<li>
<Link to="/blog/2">文章2</Link>
</li>
</ul>
<Outlet />
</div>
);
};
export default BlogList;
Outlet组件就像是一个占位符,当匹配到子路由时,子路由对应的组件就会渲染在这个位置。
默认二级路由:让页面加载更智能
有时候,我们希望在进入父路由页面时,能默认显示一个子路由页面,这就需要配置默认二级路由。还是以上述博客应用为例,我们希望进入/blog页面时,默认显示一篇文章的详情。
在路由配置中,可以通过将index作为子路由路径来实现:
import { Routes, Route } from'react-router-dom';
import BlogList from '../Page/BlogList/index';
import BlogDetail from '../Page/BlogDetail/index';
const RouterConfig = () => {
return (
<Routes>
<Route path="/blog" element={<BlogList />}>
<Route index element={<BlogDetail />} />
<Route path=":blogId" element={<BlogDetail />} />
</Route>
</Routes>
);
};
export default RouterConfig;
这样,当访问/blog路径时,会默认渲染BlogDetail组件,而当访问/blog/1等具体文章 ID 路径时,也会渲染BlogDetail组件并传递相应的文章 ID 参数。
404 路由配置:优雅处理不存在的路径
当用户输入了不存在的路径时,为了给用户更好的体验,我们需要配置 404 路由来处理这种情况。在路由配置中,将匹配所有路径的*作为最后一个路由规则:
import { Routes, Route } from'react-router-dom';
import Home from '../Page/Home/index';
import About from '../Page/About/index';
import NotFound from '../Page/NotFound/index';
const RouterConfig = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
};
export default RouterConfig;
NotFound组件可以自定义页面内容,比如显示 “页面未找到” 等提示信息,这样当用户访问不存在的路径时,就会显示这个 404 页面,避免出现空白或错误页面。
两种路由方式:BrowserRouter 与 HashRouter 的深度剖析
在 React Router 中,常用的路由方式有BrowserRouter和HashRouter,它们各有特点,适用于不同的场景。
BrowserRouter
BrowserRouter基于 HTML5 的历史 API,它的 URL 看起来更加简洁美观,符合常规的 URL 格式,例如<https://example.com/about>。在使用BrowserRouter时,服务器需要进行相应的配置,以确保刷新页面时能够正确返回对应的资源,否则可能会出现 404 错误。它适用于后端能够配合进行配置,对 URL 美观性有较高要求的项目。
HashRouter
HashRouter使用 URL 中的哈希部分(即#后面的内容)来管理路由,例如<https://example.com/#/about>。哈希部分的变化不会触发页面的重新加载,而且不需要服务器进行额外的配置,因为服务器不会处理哈希部分的内容。它适合一些对服务器配置有限制,或者不需要后端过多配合的小型项目。
在实际开发中,我们可以根据项目的具体需求和后端的支持情况,选择合适的路由方式。
结语
通过以上对 React Router 各个方面的详细讲解,相信你已经对 React Router 有了全面深入的理解。无论是简单的页面跳转,还是复杂的嵌套路由和参数传递,React Router 都能轻松应对。在实际项目中,灵活运用这些知识,能够帮助我们构建出体验良好、功能强大的单页应用。