Vue学习第33天——路由守卫(导航守卫)超详解讲解及使用场景、案例练习

一、路由守卫相关知识

1、概念

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。

2、作用

对路由进行权限控制

3、分类

全局守卫、独享守卫、组件内守卫

二、全局守卫详解

全局守卫又分为全局前置守卫和全局后置守卫

1、全局前置守卫

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
① 执行时间
初始化时执行,每次路由切换前执行
② 使用场景
全局前置守卫通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等。
③ 使用方法
可以使用 router.beforeEach 注册一个全局前置守卫

const router = new VueRouter({ ... })

router.beforeEach((to, from,next) => {
  //to: 即将要进入的目标路由
  //from:当前导航正要离开的路由
  // next():进行下一个路由
  
})

2、全局后置守卫

① 执行时间
初始化时执行,每次路由切换后执行
② 使用场景
对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
③ 注意项
不会接受 next函数,也不会改变导航本身
④ 使用方法
可以使用 router.afterEach 注册一个全局后置守卫

const router = new VueRouter({ ... })

router.afterEach((to, from) => {
  document.title = to.meta.title //修改网页的title
})

三、路由独享守卫详解

① 执行时间
独享守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。它们只有在 从一个不同的 路由导航时,才会被触发。
② 注意项
独享守卫没有后置,可以全局后置路由守卫搭配使用
③ 使用方法
直接在路由配置上使用 beforeEnter定义独享守卫

const routes = [
  {
    path: '/cats',
    component: Cats,
    beforeEnter: (to, from,next) => {
      if(to.meta.isAuth) {
      	next()
      }else {
      	alert('暂无权限')
      }
    },
  },
]

四、组件内守卫详解

组件内直接定义路由导航守卫
组件内守卫又分为进入守卫和离开守卫

1、进入守卫

① 执行时间
通过路由规则进入该组件时被调用

② 注意点
进入守卫不能访问this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

③ 使用方法
可以使用 beforeRouteEnter 注册一个进入守卫

  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !因为当守卫执行时,组件实例还没被创建!
  },

④ 可以传一个回调给next来访问组件实例
可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

2、离开守卫

① 执行时间
通过路由规则离开该组件时被调用
② 使用场景
离开守卫通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消
③ 使用方法
可以使用 beforeRouteLeave 注册一个离开守卫

beforeRouteLeave (to, from, next) {
  const answer = alert('还未保存,确定要离开吗')
  if (answer) {
  	next() //放行
  }else {
  	return false //取消
  }
}

五、案例练习

知识补充
meta:路由元信息,即程序员自定义信息,在路由规则中配置

{
	name:"miaomiao",
	path:'/cats',
	component:Cats,
	//路由元信息,是否需要权限
	meta:{isAuth:true}
}

案例需求:dogs.vue、catsAbout.vue、OwnerAbout.vue三个路由组件需要权限展示
采用bootstrap样式,可以在public/index.html中引入bootstrap样式

router/index.js代码

import Vue from "vue";
import VueRouter  from "vue-router";

Vue.use(VueRouter);

import Cats from "../pages/Cats";
import Dogs from "../pages/Dogs";
import CatsAbout from "../pages/CatsAbout";
import OwnerAbout from "../pages/OwnerAbout"

const router = new VueRouter({
    routes:[
        {
            name:"miaomiao",
            path:'/cats',
            component:Cats,
            children:[
                {
                    name:"miaoAbout",
                    path:'catsAbout',
                    component:CatsAbout,
                    meta:{isCat:true}
                },
                {
                    name:"zhurenAbout",
                    path:'/ownerAbout',
                    component:OwnerAbout,
                    //路由元信息,是否需要权限
                    meta:{
                        isAuth:true,
                        title:'铲屎官相关信息'
                    }
                }
            ]
        },
        {
            name:"wangwang",
            path:'/dogs',
            component:Dogs,
            meta:{isDog:true},
            //独享守卫:逻辑与全局前置守卫一样
            beforeEnter(to,from,next){
                if(to.meta.isDog){
                    if(localStorage.getItem('dogName') == 'dog') {
                        next()
                    } else {
                        alert('暂无权限查看狗狗相关信息')
                    }
                }
            }
        },

    ]
})

//全局前置路由守卫 :验证登录信息
router.beforeEach((to,from,next)=>{
    if(to.meta.isAuth) {
        //铲屎官相关路由组件需要验证name是否为liqi6limi
        if(localStorage.getItem('name') == 'liqi6limi') {
            next()
        } else {
            alert('暂无权限查看铲屎官相关信息')
        }
    }else {
        next()
    }
})

//全局后置路由守卫:修改网页title
router.afterEach((to,from)=>{
    //修改网页标题
    if(to.meta.title) {
        document.title = to.meta.title
    }else {
        document.title = '动物学院网站';
        console.log(from);
    }
})

export default router;

App.vue代码

<template>
  <div id="app">
    <h3>动物学院</h3>
      <button @click="catSchool" class="btn btn-info active" >喵喵院系</button>
      <button @click="dogSchool" class="btn btn-info active" >汪汪院系</button>
      <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App',
  methods: {
    catSchool(){
      this.$router.push({
        name:"miaomiao"
      },() => {})
    },
    dogSchool() {
        this.$router.push({
        name:"wangwang"
      },() => {})
    }
  }
}
</script>
<style >
  button {
    margin: 5px;
  }
 #app {
  width: 100%;
  text-align: center;
 }
   li {
    line-height: 40px;
    height: 40px;
  }
  span {
    margin-right: 5px;
  }
</style>

Dogs.vue代码

<template>
  <div>
    <h3>汪汪学院相关人员</h3>
    <p>目前还没有养狗,等我有钱了再养一只修狗</p>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name:"Dogs",
}
</script>

Cats.vue代码

<template>
  <div>
    <h3>喵喵学院相关人员</h3>
    <button class="btn btn-success" @click="catAbout">喵喵相关</button>
    <button class="btn btn-success" @click="ownerAbout">铲屎官相关</button>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name:"Cats",
  methods:{
    catAbout(){
      this.$router.push({
        path:"/cats/catsAbout",
      },()=>{})
    },
    ownerAbout(){
       this.$router.push({
        name:"zhurenAbout",
      },()=>{})
    }
  }
}
</script>

CatsAbout.vue代码

<template>
  <div>
        <ul class="list-unstyled">
      <li v-for="(cat,index) in cats" :key="index">
        <span>喵喵名:{{cat.name}}</span>
        <span>性别:{{cat.sex}}</span>
        <span>年龄:{{cat.age}}</span>
        <span>其他:已绝育,疫苗已打</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name:"CatsAbout",
  data(){
    return {
      cats:[{
        name:"憨瓜",
        age:"4岁整",
        sex:"公"
      },{
        name:"波妞",
        age:"3岁3个月",
        sex:"母",
      }]
    }
  },
  //进入守卫:通过路由规则进入组件时被调用
  beforeRouteEnter(to,from,next){
    if(to.meta.isCat){
        if(localStorage.getItem('catName') == 'cat') {
            next()
        } else {
            alert('暂无权限查看猫猫相关信息')
        }
    }
  },
  //离开守卫:通过路由规则离开时被调用
  beforeRouteLeave(to,from,next){
    console.log(to,from);
    next()
  },
}
</script>

OwnerAbout.vue代码

<template>
    <div>
        <h4>本人养有2只猫:憨瓜和波妞,等我有钱了再养一只狗</h4>
    </div>
</template>
<script>
    export default ({
       name:"OwnerAbout",
    })
</script>

运行结果
在这里插入图片描述

六、总结

1、独享守卫beforeEnter 只有前置没有后置,可以搭配全局后置守卫afterEach使用

2、进入守卫beforeRouteEnter 是支持给 next 传递回调的唯一守卫

3、组件内守卫与全局守卫的区别
全局守卫是进入前和进入后,组件内守卫是进入前和出来后,组件守卫也可以搭配全局后置守卫afterEach使用

4、路由守卫使用场景
① 全局前置守卫beforeEach通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等
② 全局后置守卫afterEach对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
③ 离开守卫beforeRouteLeave通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消

5、每个守卫方法接收3个参数
to::route, 即将要进入的目标路由;
from: route,当前导航正要离开的路由;
next:function, 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

next 可以返回的值如下:
next(): 进行下一个路由。如果全部路由执行完了,则导航的状态就是 confirmed (确认的)

next(false):取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址

next('/') next({ path: '/' }):通过一个路由地址跳转到一个不同的地址,就像调用 router.push() 一样,当前的导航被中断,然后进行一个新的导航,就和 from 一样

next(error):(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调

七、路由守卫解析流程

1、导航被触发
2、在失活的组件里调用 离开守卫 beforeRouteLeave
3、调用 全局前置守卫 beforeEach
4、在重用的组件里调用 更新守卫beforeRouteUpdate (2.2+)(举例来说,对于一个带有动态参数的路径 /user/:id,在 /users/1/users/2 之间跳转的时候,由于会渲染同样的 UserDetail 组件,因此组件实例会被复用。而这个守卫就会在这个情况下被调用)
5、在路由配置规则里调用 独享守卫 beforeEnter
6、解析异步路由组件
7、在被激活的组件里调用 进入守卫 beforeRouteEnter
8、调用全局解析守卫 beforeResolve (2.5+)(和 全局前置守卫router.beforeEach 类似,因为它在每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用)
9、导航被确认
10、调用全局后置守卫 afterEach
11、触发 DOM 更新
12、调用 进入守卫 beforeRouteEnter中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值