持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
前言:
需求:页面分为公开页面和私有页面,需要去验证页面来跳转到指定链接
流程:public公开页面--login登陆页面--private私有页面
在私有页面回退,实现从private回退到public,越过login
(因为已经登陆过了,再返回的时候就不需要登录了)
守卫核心技术:重定向
初级测试
先写两个链接,分别对应两个路由地址。
<ul>
<li>
<Link to="/public">public</Link>
</li>
<li>
<Link to="/private">private</Link>
</li>
</ul>
public所有游客都能访问---private受保护的页面
路由记得放switch唯一匹配然后再写地址对应的组件哦
公共:
function Public(){
return(
<div>public</div>
)
}
私有页面:
function Private(){
return(
<div>Private</div>
)
}
登陆页面:
function Login(){
return(
<div>
<div>login</div>
<button>登录</button>
</div>
)
}
先写一个变量来代表是否登录:
const auth={
isLogin:true
}
在路由匹配时有render的方法,里面写函数,该函数相等于一个组件。
在render写三元来判断应该显示的组件,即--已登陆就进入私有的页面中,未登陆就进入登陆页面中:
<BrowserRouter>
<ul>
<li>
<Link to="/public">public</Link>
</li>
<li>
<Link to="/private">private</Link>
</li>
</ul>
<Switch>
<Route path="/public" component={Public} />
<Route path="/private" render={()=>{ //在这里写的!
return(
auth.isLogin?<Privite/>:<Login />
)
}}/>
</Switch>
</BrowserRouter>
成功完成初级路由守护方法。
但是受保护的路由很多,每次都写在render函数中会显得很乱吧,所以我们考虑去自定义路由,使用组件来代表路由守卫:
稍微高级测试
//children就是《PrivateRoute》中包裹的组件
//rest是其他杂七杂八的东西暂时用不上
// PrivateRoute守卫组件
function PrivateRoute({children,...rest}){
return(
<Route/>
)
}
用组件来写路由守卫的话,组件用法就像Route一样,也会有path。当url为/private时,就可以显示路由守卫内children作为的新组件。守卫哪个就包裹哪个。
例如:PrivateRoute中的children为Private组件
<Route path="/public" component={Public} />
<PrivateRoute path="/private">
// 包裹被守卫的组件
<Private/>
</PrivateRoute>
同样:当路径为/private时判断isLogin的状态从而加载组件。
function PrivateRoute({children,...rest}){
return(
<Route {...rest} render={()=>{
return (
auth.isLogin?children:<Login />
)
}}/>
)
}
已完成稍微高级的路由守卫。
以上的两种写法,都是在/private的url跳转之时去判断isLogin的状态,去选择应该加载的组件。
即---实现的是:在/private的地址下进行的替换 -> private组件被替换为login组件
继续想,是否可以在抵达/private之前去跳转地址到/login地址,而不是进行组件替换呢。
(此处地址,都是浏览器的url栏的地址,而非route内部的path)
最后:redirect重定向
实现地址拦截
同样的,在跳转/private时使用三元判断登录是true还是false的状态,但不同的是:当isLogin为false时,要将path重定向到/login上去登录,直接跳转地址栏。在react-router-dom引入redirect组件。
路由守卫代码:
function PrivateRoute({children,...rest}){
return(
<Route {...rest} render={()=>{
return(
auth.isLogin?children:<Redirect to={{
pathname:"/login", //没登陆就直接重定向去/login下
}} />
)
}}/>
)
}
这样就实现了路由拦截。
拦截之后,在登陆页面中的组件写组件和点击事件。组件就是按钮。
事件就是当点击按钮时isLogin:true登陆成功,1s之后去跳转到被拦截之前应跳转到的/private页面。
因为涉及到1s之后跳转,所以写一个函数来回调。
const auth={
isLogin:false,
login(callback){
auth.isLogin = true
setTimeout(callback,1000)//一秒钟回调
}
}
写好点击事件:
function Login(){
let history = useHistory() //history方法有go,push,replace等等
return(
<div>
<button onClick={()=>{
//调用方法,执行函数
auth.login(()=>{
//从login 到 private,也就是被拦截之前应跳转到的页面
history.replace({pathname:"/private"}) //该函数在1s之后执行
})
}}>登录</button>
</div>
)
}
这样,就实现了在没登陆之前拦截进入/login页面,改变isLogin状态;点击登录之后跳转到被拦截之前的页面了。
我一直在强调被拦截之前的页面,是因为它在项目中是不固定的,是可以通过方法属性拿到的,而不是写死的。
// 在路由守卫中,可以在location下的state写好被拦截之前是哪个地址。
<Route {...rest} render={()=>{
return(
auth.isLogin?children:<Redirect to={{
pathname:"/login",
state :{from:'/private'} //在路由守卫中直接写好state
}} />
)
}}/>
回到登陆页面后可以获取这个变量
let location = useLocation()
let {from} = location.state
console.log(from,'from'); //打印/private
打印该from,可以看见被拦截之前应跳转到的页面了,索性直接将from的值给pathname去replace替换一下url就实现路由的再次跳转了。
好啦,路由守卫方法学习完啦。
react全家桶学习到此终止,期待接下来的项目与hooks的学习。
未完待续...