19-react路由守卫

222 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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/>
  )
}

image.png 用组件来写路由守卫的话,组件用法就像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就实现路由的再次跳转了。 image.png

好啦,路由守卫方法学习完啦。

react全家桶学习到此终止,期待接下来的项目与hooks的学习。

未完待续...