路由守卫
一、全局守卫
- v5 全局守卫
- v6 全局守卫
- v7 全局守卫
使用高阶组件
import { Route, Redirect } from 'react-router-dom';
// 认证守卫
const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => {
return (
<Router
render={props =>
isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />
}
{...rest}
/>
);
};
// 使用示例
<Router>
<Switch>
<Route path="/login" component={Login} />
<PrivateRoute
path="/dashboard"
component={Dashboard}
isAuthenticated={true}
/>
</Switch>
</Router>;
包装器组件
import { Navigate , useLocation } from 'react-router-dom';
// 认证守卫组件
const RequireAuth = ({children, isAuthenticated}) => {
const location = useLocation();
if(!isAuthenticated) {
return <Navigate to="/login" state={{from: location}} replace />
}
return children;
}
// 路由配置
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={
<RequestAuth isAUthenticated={true}>
<Dashboard />
</RequestAuth>
}>
</Routes>
数据路由 + 加载器守卫
import { createBrowserRouter, RouterProvider } from 'react-router';
// 加载器函数实现守卫
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{
path: 'dashboard',
element: <Dashboard />,
loader: async () => {
// 检查认证
const isAuthenticated = await checkAuth();
if (!isAuthenticated) {
throw new Response('Unauthorized', {
status: 401,
statusText: '需要登录',
});
}
return null;
},
},
{
path: 'admin',
element: <AdminPanel />,
loader: async () => {
const user = await getCurrentUser();
if (user?.role !== 'admin') {
throw new Response('Forbidden', {
status: 403,
});
}
return { user };
},
},
],
},
{
path: '/login',
element: <Login />,
},
{
path: '*',
element: <NotFound />,
},
]);
function Root() {
return <RouterProvider router={router} fallbackElement={<Loading />} />;
}
二、局部守卫
- v5 组建内守卫
- v6 组件内守卫
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
function Dashboard() {
const history = useHistory();
const isAuthenticated = true; // 从状态管理处获取
useEffect(() => {
if (!isAuthenticated) {
history.replace('/login');
}
}, [isAuthenticated, history]);
}
return <div>仪表盘</div>;
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
// 自定义守卫 Hook
const useRouteGuard = (condition, redirectPath) => {
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
if (!condition) {
navigate(redirectPath, {
state: { from: location },
replace: true,
});
}
}, [condition, navigate, redirectPath, location]);
};
// 在组件中使用
function AdminPanel() {
const { user } = useAuth();
useRouteGuard(user?.role === 'admin', '/unauthorized');
return <div>你好,管理员</div>;
}
三、 404 页面
- v5 404 页面配置
- v6 404 页面
- v7 404
<Switch>
<Route path="/" component={Home} exact />
<Route path="/about" component={About} />
<Route component={NotFound}>
</Switch>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/** 404 路由 */}
<Route path="*" element={<NotFound />} />
</Routes>
数据路由 + 加载器守卫
import { createBrowserRouter, RouterProvider } from 'react-router';
// 加载器函数实现守卫
const router = createBrowserRouter([
{
path: '/login',
element: <Login />,
},
{
path: '*',
element: <NotFound />,
},
]);
function Root() {
return <RouterProvider router={router} fallbackElement={<Loading />} />;
}
四、 v7 版本的错误边界处理
import { useRouteError, isRouteErrorResponse } from 'react-router';
function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
switch (error.status) {
case 401:
return <Navigate to="/login" replace />;
case 403:
return <Unauthorized />;
case 404:
return <NotFound />;
default:
return <ErrorPage />;
}
}
return <ErrorPage />
}
// 在路由中使用
{
path: '/protected',
element: <ProtectedPage />,
loader: protectedLoader,
errorElement: <ErrorBoundary />
}