星光不问赶路人,时光不负有心人
【33.Vue Router--导航守卫
】
导航守卫
正如其名,vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route
对象来应对这些变化,或使用 beforeRouteUpdate
的组件内守卫。
全局前置守卫
你可以使用 router.beforeEach
注册一个全局前置守卫:
const router = new VueRouter({ ... })
//全局前置守卫
router.beforeEach((to,from,next) =>{
//第一个参数to,包含的内容是切换后的路由对象,也就是跳转后的路由对象
//第二个参数from,包含的内容的是切换前的路由对象,也就是跳转前的路由对象
//第三个参数next(),是否往下执行,执行的话,如果不写的话路由就不会跳转,操作将会终止
console.log('前置路由守卫',to,from,next)
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve【解析】 完之前一直处于 等待中。
① 执行时间
初始化时执行,每次路由切换前执行
② 使用场景
全局前置守卫通常用来进行路由跳转的一些信息判断,判断是否登录,是否拿到对应的路由权限等等。
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标路由对象from: Route
: 当前导航正要离开的路由next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。【进行下一个路由】next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from
路由对应的地址。next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop或router.push
中的选项。next(error)
: (2.4.0+) 如果传入next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调。
确保 next
函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。这里有一个在用户未能验证身份时重定向到 /login
的示例:
// BAD 错误示范
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// 如果用户未能验证身份,则 `next` 会被调用两次
next()
})
// GOOD 推荐写法
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
route/index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router';
//引入组件
import About from '../pages/About';
import Home from '../pages/Home';
import Cartoon from '../pages/Cartoon';
import Stars from '../pages/Stars';
import Detail from '../pages/Detail';
//创建并暴露一个路由器
const router = new VueRouter({
// 定义一些路由
// 每个路由都需要映射到一个组件。
routes: [
//配置路由路径和路由组件
{
name: 'firstabout',
path: '/about',
component: About, //要跳转到的组件
meta:{title:'关于'}
},
{
//一级路由配置
name: 'firsthome',
path: '/home',
component: Home,
meta:{title:'主页'},
children: [
{
//二级路由配置
name: 'Hcartoon',
path: 'cartoon',
component: Cartoon,
meta:{isAuth: true,title:'卡通'},
},
{
//二级路由配置
name:'secondstars',
path: 'stars',
component: Stars,
meta:{isAuth: true,title:'明星'},
children: [
{
//三级路由配置
name: 'xiangqing',
/*使用占位符声明,接收params参数,相当于说先占个位置,在路由组件中就可以按照位置进行参数传递*/
// params方法传参配置
// path: 'detail/:title/:works',
// query方法传参配置
path: 'detail',
component: Detail,
meta:{isAuth: true,title:'详情'},
//props的第三种写法,props值为函数,该函数返回的对象中每一组key-value都会通过props传给路由组件
// query方法传参配置
props($route) {
return {
// works: $route.params.works,
title: $route.query.title,
works: $route.query.works,
a: 1,
b: 'hello',
};
},
// params方法传参配置
/* props($route) {
return {
works: $route.params.works,
title: $route.params.title,
a:1,
b:'hello'
};
}, */
},
],
},
],
},
],
});
//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
console.log('前置路由守卫',to,from)
console.log('next函数',next)
//判断是否需要鉴权
if (to.meta.isAuth) {
if (localStorage.getItem('userName') === 'xujianfei') {
document.title = to.meta.title
next()
}else{
alert('用户名不对,无权限查看!')
}
}else{
document.title = to.meta.title
next()
}
})
export default router
全局后置钩子
-
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受
next
函数也不会改变导航本身: -
全局后置守卫,组件初始化时调用,每次路由切换之后调用,路由切换之后指的就是,举个例子说,当前我在A路由组件,我要切换到B路由组件,那么后置路由守卫就是在我点了切换按钮B路由组件呈现到页面中后被调用。
① 执行时间
初始化时执行,每次路由切换后执行
② 使用场景
对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
③ 注意项
不会接受 next函数,也不会改变导航本身
④ 使用方法
可以使用router.afterEach
注册一个全局后置守卫
const router = new VueRouter({ ... })
//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach( (to,from)=>{
console.log('后置路由守卫',to,from)
//修改网页的title
document.title = to.meta.title ||'小白学习路由守卫'
})
路由独享的守卫
- 组件独享守卫是在进入组件时被调用,区别就在于,想对那个路由进行权限控制就直接在其路由配置项中添加守卫,作用域也仅限于该路由
① 执行时间
独享守卫 只在进入路由时触发,不会在 params、query 或 hash 改变时触发。它们只有在 从一个不同的 路由导航时,才会被触发。
② 注意项
独享守卫没有后置,可以全局后置路由守卫搭配使用
③ 使用方法
直接在路由配置上使用beforeEnter
定义独享守卫
你可以在路由配置上直接定义 beforeEnter
守卫:【逻辑判断也在beforeEnter
里面写】
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter(to,from,next){
//逻辑判断写在此处
},
},
]
这些守卫与全局前置守卫的方法参数是一样的。
- 全局路由守卫服务的是所有路由组件,而独享路由守卫只服务于被配置的单个路由组件
组件内的守卫
当使用路由规则进入该组件或离开该组件时,就会触发组件内守卫的调用,而组件内守卫的作用于范围也仅限于该组件
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)beforeRouteLeave
const Foo = {
template: `...`,
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
beforeRouteEnter
守卫 不能 访问 this
,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next
来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
注意 beforeRouteEnter
是支持给 next
传递回调的唯一守卫。对于 beforeRouteUpdate
和 beforeRouteLeave
来说,this
已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消。
beforeRouteLeave (to, from, next) {
const answer = alert('还未保存,确定要离开吗')//放行
if (answer) {
next()//放行
} else {
next(false)//取消
}
}
唯一不同于之前的就是:BeforeRouterLeave(),它并不像全局后置守卫一样在路由组件加载完成之后调用,而是在你切换出这个组件后被调用,也就是离开这个组件后被调用,在开发中,我们也可以使用BeforeRouterLeave() 来完成某些操作,比如当我要切换出该组件事,我希望该组件的操作能暂停,而不是一直运行,只有当我切换回该组件时,又再次运行,这样的操作就可以配合着beforeRouteEnter() 来完成,也可以提高应用的性能。
完整的导航解析流程
-
导航被触发。
-
在失活的组件里调用 离开守卫
beforeRouteLeave
。 -
调用全局前置守卫
beforeEach
。 -
在重用的组件里调用更新守卫
beforeRouteUpdate
(2.2+)。举例来说,对于一个带有动态参数的路径
/users/:id
,在/users/1
和/users/2
之间跳转的时候, 由于会渲染同样的UserDetails
组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 -
在路由配置里调用 独享守卫
beforeEnter
。 -
解析异步路由组件。
-
在被激活的组件里调用 进入守卫
beforeRouteEnter
。 -
调用全局解析守卫
beforeResolve
(2.5+)。 -
导航被确认。
-
调用全局后置守卫
afterEach
。 -
触发 DOM 更新。
-
调用 进入守卫
beforeRouteEnter
中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由元信息
通过路由记录的 meta
属性可以定义路由的元信息。使用路由元信息可以在路由中附加自定义的数据,例如:
- 每个路由给予独立的标题;
- 管理后台的路由,部分页面需要限制一些访问权限;
- 通过路由来自动生成侧边栏、面包屑;
- 部分路由的生命周期需要做缓存( Keep Alive );
- 其他更多业务场景…
我们可以在导航守卫或者是路由对象中访问路由的元信息数据。
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true, title:'创建帖子'}
},
{
path: ':id',
component: PostsDetail,
// 任何人都可以阅读文章
meta: { requiresAuth: false ,title:'阅读文章'}
}
]
}
]
后续等vue2所有基础知识学完后,整个学习笔记的代码会上传到github