Vue Router 实现登录后跳转到之前相要访问的页面

简介

该功能主要用于判定用户权限,在用户无权限时重定向至登录页,并在用户完成登录后,再定向至用户之前想要访问的路由;或者用户在任意路由点击登录时,登录成功后返回当前路由。是一个很常规的小功能。

简单示例

本文仅演示路由跳转和导航守卫相关代码的实现,不包含具体的权限验证和登录请求。

实现该功能主要分为四步:

  • 在登录组件的路由配置对象中添加 props: route => ({ redirect: route.query.redirect })
  • 在登录组件中使用 props 接收参数 props: ['redirect'] ,同时在登录事件完成时判定路由参数并跳转:
props: ['redirect'],
...
handleLogin() {
  this.$store.dispatch('LOGIN', { somePramsHere })
    .then(() => {
      this.$router.push(
        this.redirect ? { path: this.redirect } : { name: 'Home' }
      );
    })
    .catch((err) => {
      // 登录失败
    });
},

在其他路由的登录入口添加相应的 rediect 参数,如首页的登录链接:

<router-link 
  :to="{
         name: 'Login',
         query: { redirect: $route.fullPath },
       }"
 ><span class="login">登录</span></router-link>

在路由守卫中添加相应的判定逻辑,当用户不具备权限时,重定向至登录页:

import router from './index';
import store from '@/store/index';
import whiteList from './whiteList';
 
router.beforeEach((to, from, next) => {
    if (!store.state.token) {
        // 未登录
        if (
            to.matched.length > 0 &&
            to.matched.some((record) => whiteList.includes(record.path))
            // 此处还可添加用户权限验证相关逻辑,如 to.matched.some(record => record.requiresAuth)
        ) {
            // 该路由不要求用户登录,继续完成导航
            next();
        } else {
            // 用户未登录,且目标路由不在白名单中,重定向至登录页
            next({ name: 'Login', query: { redirect: to.fullPath } });
        }
    else { ... // 已登录时的路由守卫判定 }
    }
}

补充用户退出时的处理

由于导航守卫只有在路由变化时才会被触发,而使用 $router.replace() 模拟刷新并不会触发导航守卫(push() 也不行),因为 VueRouter 不允许进入相同的路由,这是其内部机制,我们无法在外部干涉。

因此,只能在用户退出成功时,手动加入与导航守卫相同的判定逻辑,若在白名单之内或拥有相应的路由权限,则留在当前路由;若不符合条件,则重定向至登录页:

退出相关组件

import whiteList from '@/store/whiteList';
import routePaths from '@/store/routePaths';
 
...
handleLogoutRedirect() {
  let location = {};
  if (
    !whiteList.includes(this.routePath) &&
    !whiteList.includes(routePaths[this.routeName])
  ) {
    location = { name: 'Login', query: { redirect: this.routePath } }; // 注意这里,需要手动带上当前路径,否则重新登录后只能到首页
    this.$router.push(location);
  }
  // 当前路由在白名单之内的,不需要跳转,留在当前路由即可
},
handleLogout() {
  this.$store
    .dispatch('LOGOUT')
    .then(() => {
      this.handleLogoutRedirect();
    })
    .catch((err) => err);
},
...

注意,我在顶部导入了一个 routePaths 对象。由于搜索页等组件是带有 params 参数的,若想要判定当前路由是否属于白名单,需要使用路由配置对象中的完整 path 字符串,比如搜索页是 /search/:keyword?,那么白名单数组和 routePaths 对象里都需要使用这个字符串。此处为了匹配的方便,使用路由组件的 name 属性作为 routePaths 的 key。示例:

routePaths.js

// 其他路由若是 path 不带 params 参数的,可以不用配置,像上述代码那样分开判定即可
const routePaths = {
    Search: '/search/:keyword?',
}
export default routePaths;

进阶

在任意路由点击登录,将当前路由和路由参数都带给登录组件,登录成功后返回之前的路由并携带全部路由参数。

此处的导航守卫、以及带有登录链接或跳转至登录页功能的组件代码无需改动,只需要修改路由配置对象和登录组件。

首先,定义路由的配置对象时,将登录组件的路由 props 改为:

props: route => {
  let params = route.params, query = route.query; // 这里不能解构 route 对象,否则拿不到值
  const redirect = query.redirect;
  Reflect.deleteProperty(query, 'redirect'); // 原本的 query 对象不带 redirect 属性,因此需要删除
  return { redirect, params, query }
},

其次,登录成功时,给路由跳转添加 prams 和 query 参数

props: ['redirect', 'params', 'query'],
...
handleLogin() {
  this.$store
    .dispatch('LOGIN', { phone: this.phone, password: this.password })
    .then((data) => {
      this.$router.push(
        this.redirect
          ? { path: this.redirect, params: this.params, query: this.query }
          : { name: 'Home' }
      );
    })
    .catch((err) => {
      console.log('登录失败:', err);
    });
},

原文地址:https://www.cnblogs.com/cjc-0313/p/16825651.html