React19 + React Router v6 完整配置化路由鉴权全套代码
整体目录结构
src
├── main.jsx # 项目入口
├── App.jsx # 根组件
├── context
│ └── AuthContext.jsx # 全局登录状态 + useAuth
├── components
│ └── RequireAuth.jsx # 权限守卫组件
├── router
│ ├── index.js # 统一导出路由配置
│ ├── routes.js # 配置化路由数组(核心)
│ └── RouterRender.jsx # 递归渲染路由工具组件
└── pages
├── Login.jsx
├── Home.jsx
├── Profile.jsx
├── Admin.jsx
└── NotFound.jsx
1. src/context/AuthContext.jsx(登录状态)
js
import { createContext, useContext, useState, useEffect } from 'react'
const AuthContext = createContext(null)
export function AuthProvider({ children }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
// 页面初始化读取本地存储,保持登录
useEffect(() => {
const token = localStorage.getItem('token')
const userStr = localStorage.getItem('user')
if (token && userStr) {
setUser(JSON.parse(userStr))
}
setLoading(false)
}, [])
// 登录方法
const login = (token, userInfo) => {
localStorage.setItem('token', token)
localStorage.setItem('user', JSON.stringify(userInfo))
setUser(userInfo)
}
// 退出登录
const logout = () => {
localStorage.removeItem('token')
localStorage.removeItem('user')
setUser(null)
}
const store = {
user,
loading,
login,
logout
}
return (
<AuthContext.Provider value={store}>
{children}
</AuthContext.Provider>
)
}
// 自定义鉴权钩子
export function useAuth() {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth 必须在 AuthProvider 内部调用')
return ctx
}
2. src/components/RequireAuth.jsx(权限拦截守卫)
js
import { Navigate, Outlet, useLocation } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
/**
* 路由鉴权守卫
* @param {boolean} needAdmin 是否需要管理员权限
*/
export default function RequireAuth({ needAdmin = false }) {
const { user, loading } = useAuth()
const location = useLocation()
if (loading) return <div>权限校验加载中...</div>
// 未登录,跳转登录页,记录原本访问地址
if (!user) {
return (
<Navigate
to="/login"
state={{ from: location }}
replace
/>
)
}
// 需要管理员,但用户不是管理员
if (needAdmin && user.role !== 'admin') {
return <Navigate to="*" replace />
}
// 校验通过,渲染页面
return <Outlet />
}
3. src/router/routes.js(路由配置数组,统一管理)
js
import Login from '../pages/Login'
import Home from '../pages/Home'
import Profile from '../pages/Profile'
import Admin from '../pages/Admin'
import NotFound from '../pages/NotFound'
/**
* 路由配置项说明
* path 路由地址
* element 页面组件
* meta 路由元信息
* meta.isPublic = true 公开页面,无需登录
* meta.requiresAuth = true 需要登录
* meta.needAdmin = true 需要管理员权限
* children 嵌套子路由
*/
export const routes = [
{
path: '/login',
element: <Login />,
meta: {
title: '登录',
isPublic: true
}
},
{
path: '/',
element: <Home />,
meta: {
title: '首页',
requiresAuth: true
}
},
{
path: '/profile',
element: <Profile />,
meta: {
title: '个人中心',
requiresAuth: true
}
},
{
path: '/admin',
element: <Admin />,
meta: {
title: '管理员后台',
requiresAuth: true,
needAdmin: true
}
},
{
path: '*',
element: <NotFound />,
meta: {
title: '404页面',
isPublic: true
}
}
]
4. src/router/RouterRender.jsx(递归渲染路由)
自动根据 meta 元信息判断是否包裹权限守卫,支持嵌套路由
js
import { Route } from 'react-router-dom'
import RequireAuth from '../components/RequireAuth'
export default function RouterRender({ routes }) {
return routes.map(routeItem => {
const { path, element, meta, children } = routeItem
// 公开页面,不做权限拦截
if (meta.isPublic) {
return (
<Route key={path} path={path} element={element}>
{children && RouterRender({ routes: children })}
</Route>
)
}
// 需要登录鉴权的路由,外层套守卫
return (
<Route
key={path}
path={path}
element={<RequireAuth needAdmin={meta.needAdmin} />}
>
<Route index element={element} />
{children && RouterRender({ routes: children })}
</Route>
)
})
}
5. src/router/index.js(统一导出入口)
js
export { routes } from './routes'
export { default as RouterRender } from './RouterRender'
6. src/App.jsx(根组件,极简配置式渲染)
js
import { Routes } from 'react-router-dom'
import { AuthProvider } from './context/AuthContext'
import { routes, RouterRender } from './router'
export default function App() {
return (
{/* 全局登录状态注入 */}
<AuthProvider>
<Routes>
{/* 一行渲染全部配置化路由 */}
<RouterRender routes={routes} />
</Routes>
</AuthProvider>
)
}
7. src/main.jsx(React19 标准入口)
js
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
)
8. 页面组件代码
pages/Login.jsx
js
import { useNavigate, useLocation } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
export default function Login() {
const navigate = useNavigate()
const location = useLocation()
const { login } = useAuth()
// 获取登录后要跳转的页面
const targetPath = location.state?.from?.pathname || '/'
// 普通用户登录
const loginUser = () => {
const token = 'user-token-001'
const userInfo = {
id: 1001,
name: '普通用户',
role: 'user'
}
login(token, userInfo)
navigate(targetPath, { replace: true })
}
// 管理员登录
const loginAdmin = () => {
const token = 'admin-token-999'
const userInfo = {
id: 9999,
name: '超级管理员',
role: 'admin'
}
login(token, userInfo)
navigate(targetPath, { replace: true })
}
return (
<div style={{ padding: '100px 30px' }}>
<h2>系统登录</h2>
<button onClick={loginUser} style={{ marginRight: 10, padding: '6px 12px' }}>
普通用户登录
</button>
<button onClick={loginAdmin} style={{ padding: '6px 12px' }}>
管理员登录
</button>
</div>
)
}
pages/Home.jsx
js
import { useAuth } from '../context/AuthContext'
import { useNavigate } from 'react-router-dom'
export default function Home() {
const { user, logout } = useAuth()
const navigate = useNavigate()
const signOut = () => {
logout()
navigate('/login', { replace: true })
}
return (
<div style={{ padding: '30px' }}>
<h2>首页</h2>
<p>欢迎您,{user.name}</p>
<p>账号身份:{user.role === 'admin' ? '管理员' : '普通用户'}</p>
<div style={{ margin: '20px 0' }}>
<button onClick={() => navigate('/profile')} style={{ marginRight: 8 }}>
个人中心
</button>
<button onClick={() => navigate('/admin')}>
管理员后台
</button>
</div>
<button onClick={signOut}>退出登录</button>
</div>
)
}
pages/Profile.jsx
js
import { useAuth } from '../context/AuthContext'
export default function Profile() {
const { user } = useAuth()
return (
<div style={{ padding: '30px' }}>
<h2>个人中心</h2>
<p>用户ID:{user.id}</p>
<p>用户名:{user.name}</p>
<p>角色:{user.role}</p>
</div>
)
}
pages/Admin.jsx
js
export default function Admin() {
return (
<div style={{ padding: '30px' }}>
<h2>管理员后台</h2>
<p>该页面仅管理员账号可访问</p>
</div>
)
}
pages/NotFound.jsx
js
import { useNavigate } from 'react-router-dom'
export default function NotFound() {
const navigate = useNavigate()
return (
<div style={{ padding: '30px' }}>
<h2>404 页面不存在</h2>
<button onClick={() => navigate('/')}>返回首页</button>
</div>
)
}
功能特性总结
- 完全配置化 :所有路由统一写在
routes.js,新增页面只添加一条配置,不改动 JSX - 分层鉴权
- 公开页面:登录/404 直接放行
- 基础鉴权:首页、个人中心(必须登录)
- 角色鉴权:Admin 页面需要 admin 角色
- 登录回跳:未登录访问受保护页面,登录后自动回到目标页面
- 持久化登录:localStorage 存储 token、用户信息,刷新页面不失效
- 支持嵌套路由:RouterRender 递归渲染,后台多级菜单直接扩展 children
- 无重复模板代码,工程化标准写法,便于后续对接后端动态路由