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

全局前置守卫

可以使用 router.beforeEach 注册一个全局前置守卫。

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中

每个守卫方法接收两个参数:

  • to: 即将要进入的目标 
  • from: 当前导航正要离开的路由 

可以返回的值如下:

  • false: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • 一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像调用 router.push() 一样,你可以设置诸如 replace: true 或 name: 'home' 之类的配置。当前的导航被中断,然后进行一个新的导航,就和 from 一样。

示例:

在本示例中使用router.beforeEach作为拦截,使用mate设定部分路由必须经过验证。验证规则为判断cookie中auth值是否存在。对不符合条件的请求,转到/login上。

js/getCookie.js

获取指定cookie值,如果不存在,则返回null

function getCookie (name) {
    const cookies = document.cookie.split('; ');

    for (const cookie of cookies) {
        const [cookieName, cookieValue] = cookie.split('=');
        if (cookieName === name) {
            return cookieValue;
        }
    }

    return null; // 如果没有找到匹配的 Cookie,返回 null 或其他默认值
}
export {getCookie}


router/index.js

路由设定:

  • / /user /login无需登录即可访问
  • /user/list /user/detail 需要cookie中有指定值(模拟登录状态)才可访问 
  • 不能直接访问的跳转到Login
import { createRouter, createWebHashHistory } from 'vue-router'
import Root from '../views/RootView.vue'
import Users from '../views/usersview.vue'
import UserList from '../views/User/UserList.vue'
import UserDetail from '../views/User/UserDetail.vue'
import Login from '../views/LoginView.vue'
import { getCookie } from '../js/getCookie'

const routes = [
    {
        path: '/',
        component: Root
    },
    {
        path: '/login',
        name: 'login',
        component: Login
    },
    {
        path: '/user',
        component: Users,
        // meta:{
        //     LoginRequire:true
        // },
        children: [
            {
                path: 'list',
                component: UserList,
                meta: {
                    LoginRequire: true
                }
            },
            {
                path: 'detail/:userid',
                component: UserDetail,
                meta: {
                    LoginRequire: true
                },
            }
        ]
    }
]


const myCookieValue = getCookie('auth');

const router = createRouter({
    history: createWebHashHistory(),
    routes
})

router.beforeEach((to, from, next) => {

    if (to.name !== 'login' && !myCookieValue && to.meta.LoginRequire) {
        console.log('jump to login')
        next({
            name: 'login',
            query: {
                from: from.fullPath
            }
        })
    }
    else {
        next()
    }
}
)
export default router

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {

  sendToAnalytics(to.fullPath)

})

它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。

它们也反映了 navigation failures 作为第三个参数:

router.afterEach((to, from, failure) => {

  if (!failure) sendToAnalytics(to.fullPath)

})

路由独享的守卫

你可以直接在路由配置上定义 beforeEnter 守卫:

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。它们只有在 从一个不同的 路由导航时,才会被触发。


组件内的守卫

最后,你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)

可用的配置 API

你可以为路由组件添加以下配置:

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
const UserDetails = {
  template: `...`,
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

在之前的文档中使用watch对相同组件内的路由切换进行处理,在这里可以使用beforeRouteUpdate()进行同样操作。

对于组件式则使用onBeforeUpdate来实现

// watch(() => route.params, async (newUserId) => {
//     await fetchData(newUserId);
// });
onBeforeRouteUpdate(async(to,from)=>{
    await fetchData(to.params.userid)
})