Skip to content

React Router

介绍

React Router 是一个用于处理路由的库,它提供了一套用于处理路由的组件和 API,使得在 React 应用中实现路由功能更加方便和灵活。

安装

bash
npm i react-router-dom
详细说明
  • v17 -> react-router-dom@5
  • v18 -> react-router-dom@6
  • v19 -> react-router-dom@7

项目搭建

bash
/src
  /router
    index.jsx
  App.jsx
  main.jsx
jsx
import { createHashRouter } from 'react-router-dom'
import React, { Suspense } from 'react'

import Home from '../views/Home.jsx'
const About = React.lazy(() => import('../views/About.jsx'))

const router = createHashRouter([
  {
    path: '/',
    element: <Home />,
  },
  {
    path: '/about',
    element: (
      <Suspense fallback={<div>Loading...</div>}>
        <About />
      </Suspense>
    ),
  },
])

export default router
jsx
import { RouterProvider } from 'react-router-dom'
import router from './router'

function App() {
  return <RouterProvider router={router}></RouterProvider>
}

export default App
路由懒加载

如上,使用 React.lazy 引入组件,还可以使用 Suspense 包裹,指定 fallback 为加载提示组件。

路由模式

常见的路由模式:

  1. HTML5 模式:例如 /home。且需后端支持。
  2. Hash 模式:例如 /#/home。兼容性最好。
  3. Memory 模式:适用于非浏览器环境(如 React Native)。
  4. Static 模式:适用于服务端渲染(SSR)。
jsx
import { createBrowserRouter, createHashRouter, createMemoryRouter, createStaticRouter } from 'react-router-dom'

const router = createBrowserRouter([
  // ...
])
jsx
import { BrowserRouter, HashRouter, MemoryRouter, StaticRouter } from 'react-router-dom

function App() {
  return (
    <BrowserRouter>
      {/* ... */}
    </BrowserRouter>
  )
}

创建路由

Data Router

createBrowserRouter + <RouterProvider>

jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

// 或者 createHashRouter, createMemoryRouter
const router = createBrowserRouter(
  [
    // ...
  ],
  {
    basename: '/app', // 可选,路由基路径
  },
)

function App() {
  return <RouterProvider router={router} />
}

UI Router

<BrowserRouter> + <Routes>

jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Home from './views/Home'
import About from './views/About'

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  )
}

<BrowserRouter> + useRoutes

jsx
import { BrowserRouter, useRoutes } from 'react-router-dom'

function AppRoutes() {
  const routes = useRoutes([
    // ...
  ])
  return routes
}

function App() {
  return (
    <BrowserRouter>
      <AppRoutes />
    </BrowserRouter>
  )
}

路由导航

声明式

常见三种组件:

  1. <Link>:基础跳转。
  2. <NavLink>:导航跳转(支持高亮)。
  3. <Navigate>:自动重定向。
jsx
import { Link, NavLink, Navigate } from 'react-router-dom'

function Home() {
  return (
    <div>
      {/* 普通跳转 */}
      <Link to="/about">关于页</Link>

      {/* 导航跳转(支持高亮) */}
      <NavLink to="/about" className={({ isActive }) => (isActive ? 'active' : '')}>
        关于页
      </NavLink>

      {/* 自动重定向 */}
      <Navigate to="/about" replace />
    </div>
  )
}

export default Home

编程式

常使用 useNavigate 钩子实现编程式导航:

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

function Home() {
  const navigate = useNavigate()

  return (
    <div>
      <h1>Home</h1>
      <button onClick={() => navigate('/about')}>关于页</button>
    </div>
  )
}

export default Home

路由接收参数

search params

jsx
// 如:/user?id=12&name=vfanlee
import { useSearchParams } from 'react-router-dom'

const [params] = useSearchParams()
const id = params.get('id')
const name = params.get('name')

path params

jsx
// 如:/user/:id/:name
import { useParams } from 'react-router-dom'

const params = useParams()
const id = params.id
const name = params.name

路由激活

<NavLink> 会自动为当前激活的链接添加 active 类:

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

function Navbar() {
  return (
    <nav>
      <NavLink to="/home" className={({ isActive }) => (isActive ? 'active' : '')}>
        Home
      </NavLink>
      <NavLink to="/about" className={({ isActive }) => (isActive ? 'active' : '')}>
        About
      </NavLink>
    </nav>
  )
}

useMatch

如果你用的是动态路径,比如 /users/:id,可以使用 useMatch 来匹配当前路由:

jsx
import { useMatch, Link } from 'react-router-dom'

function Navbar() {
  const match = useMatch('/users/:id')

  return (
    <Link to="/users/123" className={match ? 'active' : ''}>
      User Detail
    </Link>
  )
}

useLocation

使用 useLocation 获取当前路径名,再手动判断:

注意:location.pathname 是完整路径,不带 query 或 hash。

jsx
import { useLocation, Link } from 'react-router-dom'

function Navbar() {
  const location = useLocation()

  return (
    <nav>
      <Link to="/home" className={location.pathname === '/home' ? 'active' : ''}>
        Home
      </Link>
      <Link to="/about" className={location.pathname === '/about' ? 'active' : ''}>
        About
      </Link>
    </nav>
  )
}

路由配置

createBrowserRouter 为例。

basename

basename 用于指定路由的基路径:

jsx
const router = createBrowserRouter(
  [
    // ...
  ],
  {
    // 所有路由都会以 /app 开头
    basename: '/app',
  },
)

配置项

  • path:路由路径。
  • element:路由对应的组件。
  • children:子路由配置数组。
  • index:是否为默认路由。
  • errorElement:加载出错时显示的组件。
  • loader:数据加载函数。
  • action:表单提交处理函数。
  • handle:自定义路由句柄,可用于传递任意数据。

动态路由

jsx
{
  path: '/users/:id',
  element: <UserDetail />
}

匹配 404

jsx
{
  path: '*',
  element: <NotFound />
}

嵌套路由

  1. 在路由配置文件中通过 children 属性配置子路由
  2. 在父组件中通过 <Outlet> 组件渲染子路由
jsx
{
  path: '/',
  element: <Layout />,
  children: [
    {
      path: '/home',
      element: <Home />
    },
    {
      path: '/about',
      element: <About />
    }
  ]
}
jsx
import { Outlet } from 'react-router-dom'

function Layout() {
  return (
    <>
      <sidebar>这是侧边栏</sidebar>
      <div>
        <header>这是头部</header>
        <main>
          <Outlet />
        </main>
        <footer>这是底部</footer>
      </div>
    </>
  )
}

export default Layout

默认路由

嵌套路由时,指定某个子路由为默认路由,可通过 index: true 指定默认路由:

jsx
{
  path: '/',
  element: <Layout />,
  children: [
    {
      index: true,
      element: <Home />
    },
    {
      path: '/about',
      element: <About />
    }
  ]
}
jsx
{
  path: '/',
  element: <Layout />,
  children: [
    {
      path: '',
      element: <Home />
    },
    {
      path: '/about',
      element: <About />
    }
  ]
}

loading 和 error

jsx
import { Suspense } from 'react'

const router = createBrowserRouter([
  {
    path: '/about',
    // 使用 Suspense 包裹异步组件,实现加载状态
    element: (
      <Suspense fallback={<div>Loading...</div>}>
        <About />
      </Suspense>
    ),
    // 加载出错时显示的组件
    errorElement: <div>加载出错了!</div>,
  },
])

loader

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

export async function authLoader() {
  const isLogin = Boolean(localStorage.getItem('token'))

  if (!isLogin) {
    throw redirect('/login')
  }

  return null
}

const router = createBrowserRouter([
  {
    path: '/login',
    element: <Login />,
  },
  {
    path: '/',
    element: <Layout />,
    loader: authLoader, // 路由级拦截
    children: [
      {
        path: 'dashboard',
        element: <Dashboard />,
      },
    ],
  },
])

action

只有在使用 <Form> 组件提交表单时,才会触发对应路由的 action 函数。

略。

handle

类似于 route meta,可以在路由对象上存储任意数据,供组件或钩子函数使用。

jsx
const router = createBrowserRouter([
  {
    path: '/admin',
    element: <Admin />,
    handle: {
      auth: true,
      role: 'admin',
      title: '后台管理',
    },
  },
])

如何读取?

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

const matches = useMatches()

const currentRoute = matches[matches.length - 1]
console.log(currentRoute.handle)

基于 MIT 许可发布