- 全局导航守卫
- beforeEach
- beforeResolve
- afterEach
- 路由导航守卫
- beforeEnter
- 组件导航守卫
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
全局导航守卫
当一个导航触发时,全局前置守卫按照创建顺序调用。
守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
参数说明:
to: Route, 即将要进入的目标 路由对象;
from: Route,当前导航正要离开的路由;
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
全局前置守卫
router.beforeEach((to,from,next)=>{
if(xxxxx){
next('/login')
}else{
next()
}
})
全局解析守卫
注意:解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用
router.beforeResolve(()=>{})
全局后置钩子
守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
案例
const whileList = ['/']
router.beforeEach((to, from, next) => {
let token = localStorage.getItem('token')
//白名单 有值 或者登陆过存储了token信息可以跳转 否则就去登录页面
if (whileList.includes(to.path) || token) {
next()
} else {
next({
path:'/'
})
}
})
路由独享导航守卫
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
beforeEnter
守卫 只在进入路由时触发,不会在 params、query 或 hash
改变时触发。例如,从 /users/2
进入到 /users/3
或者从 /users/2#info
进入到 /users/2#projects
。它们只有在 从一个不同的 路由导航时,才会被触发。
beforeEnter
作用的是目标路由对象
组件内的守卫
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
在渲染该组件的对应路由被验证前调用
不能获取组件实例 `this` !
因为当守卫执行时,组件实例还没被创建!
},
beforeRouteUpdate(to, from) {
在当前路由改变,但是该组件被复用时调用
举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
},
beforeRouteLeave(to, from) {
在导航离开渲染该组件的对应路由时调用
与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
完整的导航解析流程
- 导航被触发
- 在之前失活的组件触发
beforeRouterLeave
- 调用全局前置守卫
beforeEach
- 如果是重用的组件
/user/:id
,调用beforeRouterUpdate
更新组件 - 然后执行路由守卫
beforeEnter
- 解析异步路由组件
- 然后执行组件内的
beforeRouterEnter
- 然后会调用全局解析守卫
beforeResolve
- 这时候导航已经被确认了
- 调用全局后置钩子
afterEach
- 触发 DOM 更新
- 调用
beforeRouteEnter
守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
loadingBar 案例
<template>
<div class="wraps">
<div ref="bar" class="bar"></div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted } from 'vue'
let speed = ref<number>(1)
let bar = ref<HTMLElement>()
let timer = ref<number>(0)
const startLoading = () => {
let dom = bar.value as HTMLElement;
speed.value = 1
timer.value = window.requestAnimationFrame(function fn() {
if (speed.value < 90) {
speed.value += 1;
dom.style.width = speed.value + '%'
timer.value = window.requestAnimationFrame(fn)
} else {
speed.value = 1;
window.cancelAnimationFrame(timer.value)
}
})
}
const endLoading = () => {
let dom = bar.value as HTMLElement;
setTimeout(() => {
window.requestAnimationFrame(() => {
speed.value = 100;
dom.style.width = speed.value + '%'
})
}, 500)
}
defineExpose({
startLoading,
endLoading
})
</script>
<style scoped lang="less">
.wraps {
position: fixed;
top: 0;
width: 100%;
height: 2px;
.bar {
height: inherit;
width: 0;
background: blue;
}
}
</style>
main.ts
import loadingBar from './components/loadingBar.vue'
const Vnode = createVNode(loadingBar)
render(Vnode, document.body)
console.log(Vnode);
router.beforeEach((to, from, next) => {
Vnode.component?.exposed?.startLoading()
})
router.afterEach((to, from) => {
Vnode.component?.exposed?.endLoading()
})