引言
在构建复杂的 React 应用时,我们常常需要对某些页面进行权限校验,比如支付页面、用户中心等。这些页面必须在用户登录后才能访问。这时,路由守卫(Route Guard) 就派上了用场。
路由守卫
路由守卫是一种在用户访问某个路由前进行权限判断的机制。如果用户未登录或权限不足,则阻止访问并重定向到登录页或其他提示页面。
对于路由守卫当中首先我们需要在已写的App.jsx当中引入路由组件的内容
import ProtectRoute from './pages/ProtectRoute'
何时需要路由守卫
何时要校验我们的权限呢,也就是说在我们下单的时候,点赞的时候是需要登录才能进行的操作的。这时候如果有校验权限的路由出现就可以很好的解决这个问题。
让我们来添加一个pay的页面吧,也就是添加Route组件页面。
const Pay = lazy(()=>import('./pages/Pay'))
<Route path="pay" element={
<ProtectRoute>
<Pay/>
</ProtectRoute>
}/>
当我们跳转到pay页面的时候可以看到这时候页面并不会直接跳转到Pay组件页面,而是会先经过路由守卫组件。
完善路由守卫组件(鉴权组件)
一个组件当中包着一个组件被称作什么呢? 这里叫props的Children属性,让我们看看props里面的内容吧!
// 鉴权组件
const ProtectRoute = (props) => {
// console.log(props);
// 并非子组件
// children属性 提升定制性
const { children } = props;
return (
<>
{children}
</>
)
}
export default ProtectRoute
判断是否登录成功
当我们要支付的时候就需要弹出登录界面框,这时候就需要判断用户输入的内容是否正确,判断之后分别跳转进入成功的页面和失败的页面。在这里需要使用到Navigate组件,实现页面的重定向(跳转),当成功跳转之后,便进入到pay支付页面啦。
import{
Navigate
} from 'react-router-dom'
const isLogin = localStorage.getItem('isLogin') === 'true';
if(!isLogin){
return <Navigate to="/login" />
}
return children
登陆页面设计
1.首先设计一个表单
这里对很多小伙伴来说登录表单的代码实在太简单了,为此咱们就放一段普遍的登录表单代码上去。
return (
<form onSubmit={handleSubmit}>
<h1>登录界面</h1>
<input
type="text"
placeholder="请输入用户名"
required
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
<input
type="password"
placeholder="请输入密码"
required
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
<button type="submit">登录</button>
</form>
)
登录表单这块并不是要学习的重点,重点则在于如何写登录表单的逻辑。
const s
const handleSubmit = (event) =>{
event.preventDefault();
if(username === 'Happy' && password === '1314520'){
localStorage.setItem('isLogin', 'true');
}else {
alert('用户名或密码错误');
}
}
2.useLocation Hook函数
现在需要从登录页面发送请求,实现从哪里来回到哪里去,这时候我们就需要用到useLocation这个Hook函数了。
📌 基本作用:
useLocation() 返回当前页面的 location 对象,可以用来:
- 获取当前路径(pathname)
- 获取 URL 中的查询参数(search)
- 获取 hash 部分
- 获取通过编程导航时传递的状态(state)
实战代码
import{
useLocation
} from 'react-router-dom'
const {pathname} = useLocation(); // 获取当前路径
if(!isLogin){
return <Navigate to="/login" state={{ from: pathname }}/>
}
state={{ from: pathname }}作用
相信很多小伙伴看到这里都是对这个代码一脸懵B,没错小编看到这里的时候也一时转不过弯来。但我们去问下ai便可以知道这段代码的作用:
传递跳转时的状态信息,不显示在 URL 中,但可以在目标页面中通过 useLocation() 获取
3.回到Login中
import{
useNavigate, // Navigate 组件 js 跳转
useLocation
} from 'react-router-dom'
const location = useLocation();
console.log(location)
打印Login当中location当中的内容,可以看到在state当中会有咱们之前传过来的值。这时候就需要拿到state当中的值,并且通过useNavigate进行页面跳转。让我们来看看下述代码:
const location = useLocation();
const navigate = useNavigate();
const handleSubmit = (event) =>{
event.preventDefault();
if(username === 'Happy' && password === '1314520'){
localStorage.setItem('isLogin','true');
navigate(location.state.from || '/');
}else{
alert('用户名或密码错误');
}
}
在这里有个难点,如果左侧值为假(如 undefined、null 或空字符串),则使用默认值 /,确保跳转路径始终有效。在这里还可以对代码进行安全性能上的优化:
navigate(location.state.from || '/');
使用 可选链操作符(?.) ,可以安全访问嵌套属性,避免在 location 或 location.state 为 undefined / null 时抛出错误。
navigate(location?.state?.from || '/');
| 代码 | 是否安全访问? | 说明 |
|---|---|---|
location.state.from | ❌ 不安全 | 如果 location 或 location.state 不存在,会抛出 Cannot read property 'state' of undefined 等错误 |
location?.state?.from | ✅ 安全 | 如果任何一级为 null 或 undefined,表达式会直接返回 undefined 而不是报错 |
4. 页面效果展示
可以看到我们通过使用ProtectRoute组件和Login组件实现了应用当中,重要的页面应用保护,对一些重要操作页面保留了其该有的安全性。
路由代码汇总
App.jsx根组件当中:
import {
useState,
lazy,
Suspense
} from 'react'
import './App.css'
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom'
import Navigation from './components/Navigation'
import ProtectRoute from './pages/ProtectRoute'
// 函数 路由 -> Route
// 懒加载
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Pay = lazy(() => import('./pages/Pay'))
const NotFound = lazy(() => import('./pages/NotFound'))
const Login = lazy(() => import('./pages/Login'))
// import Home from './pages/Home'
// import About from './pages/About'
function App() {
return (
<>
<Router>
<Navigation />
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login />} />
{/* 鉴权 */}
<Route path="/pay" element={
<ProtectRoute>
<Pay />
</ProtectRoute>
} />
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</Router>
</>
)
}
export default App
ProtectRoute组件当中:
import {
Navigate,
useLocation
} from 'react-router-dom'
// 鉴权组件
const ProtectRoute = (props) => {
// console.log(props);
// 并非子组件
// children属性 提升定制性
const { children } = props;
const pathname = useLocation();
const isLogin = localStorage.getItem('isLogin') === 'true';
if (!isLogin) {
return <Navigate to="/login" state={{ from: pathname }} />
}
return children
}
export default ProtectRoute
Login组件当中:
import {
useState
} from 'react'
import {
useNavigate, // Navigate 组件 js 跳转
useLocation
} from 'react-router-dom'
const Login = () => {
const location = useLocation();
const navigate = useNavigate();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
if (username === 'Happy' && password === '1314520') {
localStorage.setItem('isLogin', 'true');
navigate(location?.state?.from || '/');
} else {
alert('用户名或密码错误');
}
}
return (
<form onSubmit={handleSubmit}>
<h1>登录界面</h1>
<input
type="text"
placeholder="请输入用户名"
required
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
<br />
<input
type="password"
placeholder="请输入密码"
required
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
<br />
<button type="submit">登录</button>
</form>
)
}
export default Login
导航栏Navigation当中:
import { Link } from 'react-router-dom'
const Navigation = () => {
return (
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/login">Login</Link></li>
</ul>
</nav>
)
}
export default Navigation;
其它页面的风格都是大差不差,简陋的很,我们就拿pay这个页面代码来举例就是:
import './index.css';
const Pay = () => {
return (
<div className="pay">
Pay
</div>
)
}
export default Pay
总结
路由守卫与登录跳转:打造流畅可控的前端导航
- 守卫核心:
ProtectRoute组件精准拦截,为敏感路由筑起权限屏障。 - 智能导航: 利用
Navigate与useLocation,实现登录状态下的无缝跳转(重定向至原目标页或默认页)。 - 状态传递:
state={{ from: pathname }}巧妙记录来源,确保登录后精准“归位”。
上面就是React路由守卫深度理解的全部内容了,如果大家对路由的基本使用还不清楚的话,可以看看我的上两篇文章哦!
React 路由懒加载详解:优化你的前端性能
适合小白初识React-Router的一篇文章