导航守卫

@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) => {
    // ...   
})
[] 登录认证 - 借助登录 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) => {
  // ...
})
[] 统计访问量
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,使用时应先判断再使用
浏览器的返回和前进,不会触发守卫
Summary & Homework
Summary
afterEach
beforeEach
beforeEnter
Pinia*
Homework
[] 每次导航结束后,设置页面标题 title,并让页面滚动到顶部
为路由配置 meta 属性,指定 title
如果不是每个路由都配置 meta 属性,需判断再使用;必要时,应该指定默认值
{
  path: "/mine",
  name: "mine",
  component: () => import("../views/MineView.vue"),
  meta: { showNav: true, title: "我的 - 个人中心" },
},
router.afterEach((to, from) => {
  window.scrollTo(0, 0);
  if(to.meta.title){
    document.title = to.meta.title
  }
})
[] 添加页面 NProgress - 进度条 特效
导航前开始,导航后结束
router.beforeEach((to, from, next) => {
  NProgress.start();
  next()
})
    
router.afterEach((to, from) => {
  NProgress.done();
})