创见博客
React中的路由
七崽爱吃小饼干2026/01/13阅读 0专栏 React

React 路由的实现

一、前置核心认知

  1. React 是一个「单页面应用(SPA)」的核心库,SPA 的特点:页面只有一个 HTML 文件,页面切换时不会重新请求/刷新整个页面,只是页面局部内容更新。
  2. React 路由的本质:通过监听浏览器地址栏的路径变化,匹配对应的 React 组件,然后把匹配到的组件渲染到页面指定位置,这就是 React 路由的核心实现逻辑。
  3. 路由分类:
    • 前端路由:就是 react-router-dom 实现的路由,只做「路径-组件」的匹配渲染,不会请求后端,是 React 项目的核心路由方式;
    • 后端路由:根据前端请求的 URL 路径,返回对应的 HTML/数据,和 React 路由无关。

二、第一步:安装依赖

React 路由的核心库是 react-router-dom,专门用于浏览器环境的 React 项目,执行如下命令安装(最新版默认就是 v6):

bash
npm install react-router-dom
# 或者 yarn
yarn add react-router-dom

三、React Router v6 核心基础用法(最常用,必掌握)

核心原则

react-router-dom v6 中,所有路由相关的组件,都必须包裹在 BrowserRouter /HashRouter组件内部 才能生效,否则会报错。

方式1:全局根入口包裹(src/main.jsx / src/index.js)

这是项目开发的标准写法,只需要在整个应用的根组件外层包裹一次 BrowserRouter,整个项目的所有组件都能使用路由功能,一劳永逸。

jsx
// src/main.jsx (React18 入口文件)
import React from 'react'
import ReactDOM from 'react-dom/client'
// 1. 导入核心路由容器 BrowserRouter
import { BrowserRouter } from 'react-router-dom'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    {/* 2. 包裹根组件 App,全局生效 */}
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)

四、核心路由组件

React Router v6 精简了组件,核心就 4 个最常用的路由组件,没有多余的API,全部掌握就够用99%的场景:

1. <Routes> 路由容器组件

  • 作用:路由规则的「容器」,专门用来包裹所有的 <Route> 组件,是路由规则的父容器。
  • 核心特性:v6 新增的组件,替代了 v5 的 <Switch>,会自动匹配「最佳路由规则」,匹配成功后就停止匹配(不会出现多个路由同时渲染的问题),性能更高。
  • 语法:必须包裹 <Route>,且只能直接包裹 <Route>。

2. <Route> 路由规则匹配组件

  • 作用:核心路由规则,实现「路径(path) → 组件(element)」的映射关系。
  • 核心属性:
    • path:匹配的浏览器地址栏路径(如 /home、/about);
    • element:路径匹配成功后,要渲染的 React 组件(必须是 <组件 /> 格式,不能只写组件名);
  • 特殊规则:path="*" 表示「匹配所有未定义的路径」,用于做 404页面 匹配。

3. <Link> 路由导航组件

  • 作用:实现路由页面的「跳转导航」,等价于 HTML 中的 <a href="xxx"> 标签。
  • 核心优势:<a> 标签跳转页面会刷新整个页面(不符合SPA特性),<Link> 跳转只会局部更新组件,不刷新页面,完美契合SPA。
  • 语法:to="匹配的路由path",和 <Route path="xxx"> 对应。

4. <Outlet> 嵌套路由出口组件

  • 作用:专门用于 嵌套路由 场景,是「子路由组件」的渲染占位符。
  • 通俗理解:父路由匹配的组件中,写一个 <Outlet>,那么子路由匹配的组件,就会渲染到这个 <Outlet> 的位置。

五、完整代码示例(基础路由 + 404 兜底)

步骤1:准备2个页面组件(示例)

jsx
// src/pages/Home.jsx
const Home = () => {
  return <div>我是首页 - Home 组件</div>
}
export default Home

// src/pages/About.jsx
const About = () => {
  return <div>我是关于页 - About 组件</div>
}
export default About

// src/pages/NotFound.jsx (404页面)
const NotFound = () => {
  return <div>404 页面 - 您访问的路径不存在!</div>
}
export default NotFound

步骤2:在App.jsx中配置路由规则+导航

jsx
// src/App.jsx
import { Routes, Route, Link } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import NotFound from './pages/NotFound'

function App() {
  return (
    <div className="App">
      {/* 一、路由导航:Link组件 */}
      <div style={{margin: '20px'}}>
        <Link to="/" style={{marginRight: '20px'}}>首页</Link>
        <Link to="/about">关于我们</Link>
      </div>

      {/* 二、路由规则:Routes + Route 组件 */}
      <Routes>
        {/* 匹配首页路径,渲染Home组件 */}
        <Route path="/" element={<Home />} />
        {/* 匹配关于页路径,渲染About组件 */}
        <Route path="/about" element={<About />} />
        {/* 404兜底:匹配所有未定义的路径 */}
        <Route path="*" element={<NotFound />} />
      </Routes>
    </div>
  )
}

export default App

效果测试

  1. 打开页面,地址栏是 http://localhost:5173/ → 渲染 Home 组件;
  2. 点击「关于我们」→ 地址栏变成 http://localhost:5173/about → 渲染 About 组件;
  3. 手动修改地址栏为 http://localhost:5173/xxx → 渲染 NotFound 404组件;
  4. 所有跳转都不会刷新页面,完美实现SPA!

六、进阶必备:嵌套路由

什么是嵌套路由?

就是「路由套路由」,比如:

  • 父路由:/user → 渲染 User 父组件;
  • 子路由:/user/profile → 渲染 Profile 子组件;
  • 子路由:/user/setting → 渲染 Setting 子组件; 父组件中会固定展示一些内容(如用户侧边栏),子组件的内容会根据路径变化,渲染到父组件的指定位置。

嵌套路由完整实现

步骤1:准备嵌套的页面组件

jsx
// src/pages/User.jsx (父路由组件)
import { Link, Outlet } from 'react-router-dom'
const User = () => {
  return (
    <div>
      <h2>我是用户中心(父组件)</h2>
      {/* 子路由导航 */}
      <div style={{margin: '10px 0'}}>
        <Link to="/user/profile" style={{marginRight: '10px'}}>个人资料</Link>
        <Link to="/user/setting">账户设置</Link>
      </div>
      {/* 核心:子路由组件的渲染占位符 */}
      <Outlet />
    </div>
  )
}
export default User

// src/pages/Profile.jsx (子路由组件)
const Profile = () => {
  return <div>个人资料:姓名、年龄、头像...</div>
}
export default Profile

// src/pages/Setting.jsx (子路由组件)
const Setting = () => {
  return <div>账户设置:修改密码、绑定手机号...</div>
}
export default Setting

步骤2:配置嵌套路由规则(核心:children 属性)

在 <Route> 中通过 children 属性,配置当前路由的「子路由规则」,子路由的 path 可以写相对路径(也可以写绝对路径)。

jsx
// src/App.jsx 新增路由配置
import User from './pages/User'
import Profile from './pages/Profile'
import Setting from './pages/Setting'

// 在Routes中新增:
<Route path="/user" element={<User />}>
  {/* 子路由规则:children数组包裹子Route */}
  <Route path="profile" element={<Profile />} />
  <Route path="setting" element={<Setting />} />
</Route>

步骤3:测试效果

  1. 地址栏访问 http://localhost:5173/user → 只渲染 User 父组件,<Outlet> 位置为空;
  2. 点击「个人资料」→ 地址栏变成 http://localhost:5173/user/profile → <Outlet> 位置渲染 Profile 组件;
  3. 点击「账户设置」→ 地址栏变成 http://localhost:5173/user/setting → <Outlet> 位置渲染 Setting 组件;

七、路由传参

开发中经常需要在路由跳转时传递参数(比如:列表页跳详情页,传递商品ID/用户ID),React Router v6 提供2种最常用的传参方式,满足所有场景:

方式1:params 动态路由传参(推荐,路径传参,地址栏可见)

1. 核心语法

  • 配置路由时:path="路由名/:参数名" (比如 /detail/:id、/user/:userId);
  • 跳转时:Link to="/detail/1001" (参数值直接拼在路径后);
  • 接收参数:使用 useParams() 钩子函数,直接解构获取。

2. 完整示例

jsx
// 1. 配置动态路由
<Route path="/detail/:id" element={<Detail />} />

// 2. 跳转传参
<Link to="/detail/1001">查看商品1001详情</Link>

// 3. 接收参数:src/pages/Detail.jsx
import { useParams } from 'react-router-dom'
const Detail = () => {
  const { id } = useParams() // 解构获取参数
  return <div>商品详情页,商品ID:{id}</div>
}
export default Detail

方式2:search 搜索传参(地址栏拼接,?key=value 格式)

1. 核心语法

  • 配置路由时:正常写 path="/search",不需要改路由;
  • 跳转时:Link to="/search?keyword=React&page=1";
  • 接收参数:使用 useSearchParams() 钩子函数(类似 React 的 useState)。

2. 完整示例

jsx
// 1. 配置普通路由
<Route path="/search" element={<Search />} />

// 2. 跳转传参
<Link to="/search?keyword=React&page=1">搜索React</Link>

// 3. 接收参数:src/pages/Search.jsx
import { useSearchParams } from 'react-router-dom'
const Search = () => {
  const [searchParams] = useSearchParams()
  const keyword = searchParams.get('keyword') // 获取单个参数
  const page = searchParams.get('page')
  return (
    <div>
      搜索关键词:{keyword} <br/>
      当前页码:{page}
    </div>
  )
}
export default Search

八、编程式导航

上面的 <Link> 是「声明式导航」(写在JSX中的标签跳转),开发中还有大量场景需要在JS逻辑中实现路由跳转(比如:登录成功后跳首页、表单提交后跳列表页),这种方式就是「编程式导航」。

核心API:useNavigate() 钩子函数

1. 基本使用

jsx
import { useNavigate } from 'react-router-dom'

const Login = () => {
  // 1. 调用钩子函数,得到导航函数
  const navigate = useNavigate()

  const handleLogin = () => {
    // 模拟登录逻辑
    const isLogin = true
    if (isLogin) {
      // 2. 编程式跳转:跳转到首页
      navigate('/')
    }
  }

  return (
    <div>
      <h2>登录页</h2>
      <button onClick={handleLogin}>登录成功跳首页</button>
    </div>
  )
}
export default Login

2. 编程式传参

jsx
// 方式1:params传参
navigate('/detail/1002')

// 方式2:search传参
navigate('/search?keyword=Vue&page=2')

3. 其他常用功能

jsx
navigate(-1) // 等价于浏览器的「后退」按钮,返回上一页
navigate(1)  // 等价于浏览器的「前进」按钮,前进下一页

九、React Router v5 vs v6 核心差异

很多老项目还在用 v5,面试一定会问两者的区别,记住这5个核心差异即可,都是高频考点:

  1. 路由容器不同:v5 用 <Switch> 做路由容器,v6 废弃 <Switch>,新增 <Routes> 替代;
  2. 组件渲染属性不同:v5 的 <Route> 用 component={Home} 渲染组件,v6 用 element={<Home />} 渲染组件;
  3. 嵌套路由不同:v5 嵌套路由需要写完整路径,v6 用 children 属性+<Outlet> 实现,更简洁;
  4. 导航API不同:v5 用 useHistory() 做编程式导航,v6 废弃 useHistory(),新增 useNavigate() 替代;
  5. 匹配规则不同:v6 的 <Routes> 是「精准匹配」,v5 的 <Switch> 是「模糊匹配」,v6 匹配更精准,性能更高。

总结

  1. React 本身无内置路由,路由功能完全依赖第三方库 react-router-dom;
  2. 路由核心逻辑:监听地址栏路径变化 → 匹配对应组件 → 渲染到指定位置,实现SPA局部更新;
  3. v6 核心组件:<BrowserRouter>(根容器)、<Routes>(规则容器)、<Route>(匹配规则)、<Link>(声明式导航)、<Outlet>(嵌套出口);
  4. 路由传参:params(动态路由)、search(地址栏拼接),分别用 useParams() / useSearchParams() 接收;
  5. 编程式导航:useNavigate() 钩子函数,用于JS逻辑中跳转路由;
  6. 面试必考:v6 废弃了 <Switch>、useHistory(),新增了 <Routes>、useNavigate()、<Outlet>。
评论
0/100