五、【React-Router6】路由表 useRoutes() + Outlet

本文详细介绍如何使用React Router的useRoutes方法进行路由管理,包括基本配置、嵌套路由及子路由的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、useRoutes() 介绍

  • 原来写的路由管理如下
<Routes>
    <Route path='/about' element={<About />} />
    <Route path='/home' element={<Home />} />
    <Route path='/' element={<Navigate to='/about' />} />
</Routes>
  • 使用路由表 useRoutes()
    • 正常会单独建一个 routes 文件夹,文件夹下单独维护一个路由表js文件
import { useRoutes } from 'react-router-dom'

// 定义路由表
const elements = useRoutes([{
    path: '/about',
    element: <About />
}, {
    path: '/home',
    element: <Home />
}, {
    path: '/',
    element: <Navigate to='/about' />
}])

// 在配置路由管理的地方直接插入即可
{elements}

2、简单 CODING

项目修改自 上一节 的 Demo

2.1、项目结构

在这里插入图片描述

2.2、routes.js

import { Navigate } from 'react-router-dom'
import About from '../components/About'
import Home from '../components/Home'

const routes = [{
    path: '/about',
    element: <About />
}, {
    path: '/home',
    element: <Home />
}, {
    path: '/',
    element: <Navigate to='/about' />
}]

export default routes

2.3、App.js

import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'

export default function App() {

    const activeClassName = ({ isActive }) => isActive ? 'list-group-item peiqi' : 'list-group-item'

    const elements = useRoutes(routes)

    return (
        <div>
            <div className="row">
                <div className="col-xs-offset-2 col-xs-8">
                    <div className="page-header"><h1>React Router Demo</h1></div>
                </div>
            </div>
            <div className="row">
                <div className="col-xs-2 col-xs-offset-2">
                    <div className="list-group" style={{ whiteSpace: 'pre-wrap' }}>
                        <NavLink className={activeClassName} to="/about">About</NavLink>
                        <NavLink className={activeClassName} to="/home">Home</NavLink>
                    </div>
                </div>
                <div className="col-xs-6">
                    <div className="panel">
                        <div className="panel-body" style={{ whiteSpace: 'pre-wrap' }}>
                            {/* 注册路由 */}
                            {elements}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

2.4、Result

在这里插入图片描述

3、嵌套 Outlet CODING

项目后续增加的内容修改自 七、【React-Router5】嵌套路由 的 Demo

3.1、项目结构

  • 增加了子组件

在这里插入图片描述

3.2、routes.js

  • 有子路由就加 children 属性,可以无限套娃
import { Navigate } from 'react-router-dom'
import About from '../components/About'
import Home from '../components/Home'
import Message from '../components/Home/Message'
import News from '../components/Home/News'

const routes = [{
    path: '/about',
    element: <About />
}, {
    path: '/home',
    element: <Home />,
    children: [{
        path: 'news',
        element: <News />
    }, {
        path: 'message',
        element: <Message />
    }]
}, {
    path: '/',
    element: <Navigate to='/about' />
}]

export default routes

3.3、Home.js

  • 这里需要注意的变化
    • 子路由 NavLinkto 可以像原来一样写全 path:to="/home/news"
    • 也可以直接 ./ + 子路由名:to="./news"
    • 最简单直接写子路由名:to="news"
    • 子路由组件呈现的位置直接使用 6 提供的 <Outlet> 标签声明即可,会自动分配子路由管理
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'

export default function Home() {
    return (
        <>
            <h3>我是Home的内容</h3>
            <div>
                <ul class="nav nav-tabs">
                    <li>
                        <NavLink className='list-group-item' to="/home/news">News</NavLink>
                    </li>
                    <li>
                        {/* to 可以直接 ./ + 子路由名 */}
                        <NavLink className='list-group-item' to="./message">Message</NavLink>
                    </li>
                    <li>
                        {/* to 甚至可以直接写子路由名字 */}
                        <NavLink className='list-group-item' to="other">Other</NavLink>
                    </li>
                </ul>
                {/* 指定路由组件呈现的位置 */}
                <Outlet />
            </div>
        </>
    )
}

3.4、App.js

  • 这里需要注意的变化
    • <NavLink end> end 属性可以父路由失去被选中状态,默认 false
<NavLink className={activeClassName} end to="/home">Home</NavLink>

3.5、Result

在这里插入图片描述

<think>好的,我现在需要解决用户关于react-router-dom v7中实现路由守卫的问题。用户之前可能已经了解过v6或其他版本的路由配置,现在升级到v7后,需要知道如何实现类似导航守卫的功能。首先,我得回忆一下react-router-dom v7的变化,尤其是相对于v6有哪些不同,因为路由守卫的实现方式可能因版本而异。 根据用户提供的引用,特别是引用[2]中的代码示例,里面提到了通过渲染路由配置和拦截的方式来实现守卫。不过,那些代码可能针对的是旧版本,比如v5或v6,因为v7中可能有一些API的变化。比如,在v6中,使用<Routes>和<Route>组件的方式与v5不同,而v7可能延续了v6的某些模式,但可能有进一步的调整。 首先,我需要确认react-router-dom v7的API。查阅官方文档,v7中推荐使用新的数据API,比如createBrowserRouterRouterProvider,这可能改变了传统的组件层次结构。在v6中,路由配置通常是通过<Routes>和<Route>组件嵌套实现的,而在v7中,可能更倾向于使用配置对象,类似于引用[2]中的示例,但需要适配新的API。 路由守卫的核心是在路由跳转前进行权限检查或其他逻辑。在React中,这通常涉及到在渲染Route组件之前进行条件判断。在类组件中,可能使用高阶组件(HOC)包装,而在函数式组件中,可以使用自定义钩子或包装组件。 用户提到的引用[3]中提到了路由传参和声明式导航,这可能与守卫无关,但需要确保在守卫逻辑中能访问路由参数或状态。例如,在检查用户权限时,可能需要获取当前的路由参数或路径。 接下来,考虑如何在v7中实现。假设v7延续了v6useRoutes钩子,或者使用新的配置方式。可能需要创建一个包装组件,在渲染子路由前执行验证逻辑。例如,每个需要守卫的路由可以包裹在一个自定义的RouteGuard组件中,该组件接收验证函数和回退路径作为props。 在实现过程中,需要注意以下几点: 1. 如何监听路由变化:在React Router中,可以使用useLocation或useNavigate来跟踪和控制导航。 2. 如何处理异步验证:例如,需要等待API返回用户权限状态后再决定是否允许导航。 3. 如何避免无限循环:例如,当用户未登录时重定向到登录页,但登录页本身不应再次触发重定向。 可能的解决方案包括: - 使用自定义的<ProtectedRoute>组件,包裹需要守卫的路由元素。 - 在路由配置的element属性中使用该组件,传入权限验证逻辑和回退路径。 - 在ProtectedRoute组件内部使用useEffect或useLayoutEffect来执行验证,并根据结果进行导航。 例如,路由配置可能如下: ```javascript const router = createBrowserRouter([ { path: '/', element: <ProtectedRoute checkAuth={checkUser} redirectTo="/login" />, children: [ { path: 'dashboard', element: <Dashboard /> }, // 其他受保护路由 ] }, { path: '/login', element: <Login /> } ]); ``` 其中,ProtectedRoute组件负责执行checkAuth函数,如果失败则重定向到redirectTo路径。 此外,需要考虑如何处理嵌套路由和全局守卫。可能需要将守卫逻辑应用到父路由,使其覆盖所有子路由,或者在每个需要守卫的路由上单独应用。 另外,引用[2]中的示例使用了<Switch>组件,这在v6之后已被<Routes>取代,而v7可能继续使用<Routes>,所以需要调整代码以适应新API。例如,使用useRoutes钩子来生成路由配置,或者在createBrowserRouter中定义路由结构。 最后,需要测试不同的场景,如用户直接访问受保护路由、权限变化时的处理等,确保守卫逻辑可靠。</think>在 React Router v7 中实现路由守卫(导航守卫)可通过以下步骤完成,核心思路是**通过高阶组件封装路由元素,结合路由配置的动态渲染机制**实现权限控制。以下是具体实现方式: --- ### 一、路由守卫核心实现原理 React Router v7 延续了 v6 的声明式路由风格,但推荐使用 `createBrowserRouter` 和 `RouterProvider` 进行配置[^1]。路由守卫的关键在于: 1. **拦截路由渲染过程** 2. **执行权限校验逻辑** 3. **动态返回合法路由元素或重定向** --- ### 二、具体实现步骤 #### 步骤 1:创建路由守卫高阶组件 ```javascript // ProtectedRoute.jsx import { useEffect } from 'react'; import { useNavigate, Outlet } from 'react-router-dom'; const ProtectedRoute = ({ checkAuth, redirectPath = '/login' }) => { const navigate = useNavigate(); useEffect(() => { const verifyAuth = async () => { const isAuthenticated = await checkAuth(); if (!isAuthenticated) navigate(redirectPath); }; verifyAuth(); }, [checkAuth, redirectPath, navigate]); return <Outlet />; // 渲染子路由 }; export default ProtectedRoute; ``` #### 步骤 2:配置带守卫的路由表 ```javascript // router.js import { createBrowserRouter } from 'react-router-dom'; import ProtectedRoute from './ProtectedRoute'; import Dashboard from './Dashboard'; import Login from './Login'; const router = createBrowserRouter([ { path: '/', element: <ProtectedRoute checkAuth={() => !!localStorage.getItem('token')} />, children: [ { path: 'dashboard', element: <Dashboard /> }, { path: 'profile', element: <Profile /> } ] }, { path: '/login', element: <Login /> } ]); export default router; ``` #### 步骤 3:在根组件注入路由 ```javascript // App.jsx import { RouterProvider } from 'react-router-dom'; import router from './router'; export default function App() { return <RouterProvider router={router} />; } ``` --- ### 三、关键特性说明 1. **动态鉴权逻辑** `checkAuth` 函数支持同步/异步操作,可对接后端 API 验证权限: ```javascript checkAuth={async () => { const res = await fetch('/api/check-auth'); return res.status === 200; }} ``` 2. **自动重定向机制** 使用 `navigate(redirectPath)` 实现未授权跳转,支持相对路径与绝对路径[^3] 3. **嵌套路由守卫** 通过 `<Outlet />` 渲染子级路由,天然支持多层级守卫嵌套 --- ### 四、进阶优化方案 1. **细粒度权限控制** 为不同路由设置不同校验规则: ```javascript { path: 'admin', element: <ProtectedRoute checkAuth={checkAdmin} />, children: [...] } ``` 2. **路由加载状态处理** 添加加载动画提升用户体验: ```javascript const ProtectedRoute = () => { const [isChecking, setIsChecking] = useState(true); useEffect(() => { verifyAuth().finally(() => setIsChecking(false)); }, []); return isChecking ? <Spinner /> : <Outlet />; }; ``` 3. **白名单机制** 对公共路由免校验: ```javascript const publicPaths = ['/login', '/register']; if (publicPaths.includes(location.pathname)) return <Outlet />; ``` ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纯纯的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值