一杯糯糯香柠茶从时间搞懂——React中的React Router<( ̄︶ ̄)↗[GO!]

358 阅读8分钟

在 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 都能轻松应对。在实际项目中,灵活运用这些知识,能够帮助我们构建出体验良好、功能强大的单页应用。