导航守卫

@Guards
控制路由的访问权限,如:未登录,不可以下单、发帖、留言或领取优惠券,通常会引导用户到特定页面,如登录页面
完整路由属性和方法,请访问 Router
导航解析流程
导航被触发
在失活的组件里调用 beforeRouteLeave 守卫
调用全局的 beforeEach 守卫
在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)
在路由配置里调用 beforeEnter
解析异步路由组件
在被激活的组件里调用 beforeRouteEnter
调用全局的 beforeResolve 守卫(2.5+)
导航被确认
调用全局的 afterEach 钩子
触发 DOM 更新
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入

相当于是路由的生命周期函数

假设路由配置实例如下

const router = createRouter({ ... })
beforeEach

使用 router.beforeEach 注册一个全局前置守卫,执行函数 fn:接收3个参数:

to:进入的路由

from:离开的路由

next():是一个函数,表示是否允许,实现路由控制,如:未登录不允许访问,并引导|重定向到指定页面

正常访问;或不使用 next 形参
router.beforeEach((to, from, next) => {
    next()
})
router.beforeEach((to, from,) => {
})
不允许访问 - 简单粗暴;不建议直接使用
router.beforeEach((to, from, next) => {
    if(to.path=='/order')
        next(false)
})
在next()中指定跳转的路由,引导到指定页面 - 建议
router.beforeEach((to, from, next) => {
    if (to.path == '/order') {
        next({ name: 'home' })
    } else {
        next()
    }
})
如果使用了 next(),但是不执行,则路由 不会跳转,也就不会执行全局后置钩子 afterEach()
router.beforeEach((to, from, next) => {
    console.log('hi');    
})
[] 登录认证 - 借助登录 token 或本地存储的登录信息实现路由控制
router.beforeEach((to, from, next) => {
    console.log('hi');
    let token = localStorage.getItem('token')
    if (to.path == '/order' && !token) {
        next('/login')
    } else {
        next()
    }
})
afterEach
使用 router.afterEach 注册一个全局后置守卫,执行函数 fn,接收2个参数:to、from
不接受 next 函数也不会改变导航本身,因为导航已结束
router.afterEach((to, from) => {
    //
})
[] 每次导航结束后,设置页面标题 title
需要为路由配置 meta 属性来指定 title
如果不是每个路由都配置 meta 属性,需判断再使用;必要时,应该指定默认值
router.afterEach((to, from) => {
    if(to.meta.title){
        document.title = to.meta.title
    }
})
[] 添加页面 NProgress - 进度条 特效
导航前开始,导航后结束
router.beforeEach((to, from, next) => {
    NProgress.start();
    next()
})
    
router.afterEach((to, from) => {
    NProgress.done();
})
            
[] 统计访问量
beforeEnter
路由独享的守卫 - 局部路由使用守卫
路由配置时指定
针对特定路由的控制; 只在 进入 路由时触发
动态路由对应的多个路由间不会触发,因为他们对应路由配置中同一条路由
路由守卫和状态管理
在 setup() 中,你不需要再做任何特别处理即可开箱使用 - 引入、实例、使用;这是因为全局共享一个 store 实例,main.js 中的 useStore() 给你的 app 自动注入了 pinia 实例
如果没有,则需要手动注入
//....                
import { createPinia } from 'pinia'

const pinia = createPinia()

app.use(pinia)
//这之后可以随意使用Pinia...
导航守卫运行在 Vue 应用初始化的早期阶段,此时可能还未创建或注入 Pinia store 实例;如果直接在路由中引入,会提示
"getActivePinia()" was called but there was no active Pinia.
更多信息,请访问 Pinia - 在 setup() 外部使用 store
[] 路由守卫和状态管理;实操 - 汉堡菜单
在路由文件中,引入仓库 menu.js
每次进入,创建菜单实例并设置菜单栏样式为隐藏
import { useMenuStore } from '@/stores/menu'

router.beforeEach((to, from, next) => {
    // 其它逻辑

    // MUST
    const store = useMenuStore();
    store.flag = false;
    next();
})

router.afterEach((to, from) => {
    // 其它逻辑

    if (to.path === from.path) {
        const store = useMenuStore()
        store.flag = false
    }
})
守卫中的两个 store 返回的是同一个 Pinia store 实例。虽然变量名相同,但是在不同的作用域中,引用的都是同一个对象实例,因此不会导致逻辑上的冲突
更多路由控制,应由后端服务器实现,前端不可靠
路由配置时,如果不是每个路由都指定 meta,使用时应先判断再使用