网站

Vue Router 官方文档

引入

在 Vue 项目中,用户访问不同页面时,通常需要根据当前登录状态判断是否允许进入某个路由。例如,未登录用户可以访问登录页,但不能直接访问用户中心或管理后台。

如果每个页面都单独判断登录状态,代码容易重复。Vue Router 提供了路由守卫,可以在路由跳转前统一执行检查逻辑。

正文

定义

全局前置守卫

router.beforeEach() 会在每一次路由跳转发生之前执行。它可以检查登录状态、权限和路由信息,然后决定本次跳转是否继续。

beforeEach 中的 before 表示“之前”,each 表示“每一次”。因此它表示:每次路由跳转之前都执行一次检查

路由配置可以参考 routes,而全局前置守卫通常写在创建路由实例之后。

为什么需要路由守卫

路由守卫主要用来处理进入页面前的统一逻辑:

  • 判断用户是否已经登录
  • 判断用户是否具有访问权限
  • 未登录时跳转到登录页
  • 修改页面标题
  • 记录页面访问或跳转信息

语法

router.beforeEach(function (to, from) {
  // 返回 true,允许继续跳转
  return true;
});

beforeEach() 接收一个回调函数:

  • to:准备进入的路由对象
  • from:当前离开的路由对象
  • 返回 true:允许进入目标页面
  • 返回 false:取消本次跳转
  • 返回路由地址:取消当前跳转,并跳转到新的地址

基础例子

下面的例子要求用户登录后才能进入用户中心:

router.beforeEach(function (to, from) {
  const isLogin = true;
 
  if (to.path === "/user" && !isLogin) {
    return "/login";
  }
 
  return true;
});

当用户访问 /user 时,如果 isLoginfalse,守卫会返回 /login,用户就会被带到登录页。

使用 meta 标记需要登录的路由

不建议把需要登录的路径全部写死在守卫中,可以在路由配置的 meta 中标记:

const routes = [
  {
    path: "/login",
    component: () => import("./views/Login.vue")
  },
  {
    path: "/user",
    component: () => import("./views/User.vue"),
    meta: {
      requiresAuth: true
    }
  }
];

然后在全局前置守卫中读取 to.meta.requiresAuth

router.beforeEach(function (to) {
  const isLogin = false;
 
  if (to.meta.requiresAuth && !isLogin) {
    return {
      path: "/login",
      query: {
        redirect: to.fullPath
      }
    };
  }
 
  return true;
});

这里将原本要访问的地址保存到 redirect 中,登录成功后可以再跳回原页面。

登录页白名单

登录页本身通常不能要求登录,否则用户会出现反复跳转的问题。使用 meta 标记需要登录的路由后,登录页默认不需要特殊处理:

router.beforeEach(function (to) {
  const token = localStorage.getItem("token");
 
  if (to.meta.requiresAuth && !token) {
    return "/login";
  }
 
  return true;
});

只有带有 requiresAuth: true 的路由才会检查 token,因此 /login 可以正常访问。

修改页面标题

meta 不只可以记录权限信息,也可以保存页面标题:

router.beforeEach(function (to) {
  document.title = to.meta.title || "我的网站";
  return true;
});

对应的路由配置:

const routes = [
  {
    path: "/home",
    component: () => import("./views/Home.vue"),
    meta: {
      title: "首页"
    }
  }
];

常见返回值

返回值作用
true允许本次路由跳转
false取消本次路由跳转
"/login"取消当前跳转,并跳转到登录页
{ name: "login" }按命名路由跳转到登录页
不返回内容通常表示允许继续,但建议明确写出 return true

特点

  • router.beforeEach()全局守卫,所有路由跳转都会经过它
  • to 表示准备进入的路由,from 表示当前离开的路由
  • 返回 true 才表示明确允许继续跳转
  • 返回 false 可以取消跳转
  • 返回新的路径或路由对象,可以把用户引导到其他页面
  • 可以通过 to.meta 读取路由配置中的自定义信息
  • 登录状态可以从状态管理、localStorage 或其他数据源中读取
  • 守卫中不要重复跳转到当前正在访问的页面,否则可能产生无限重定向

常见错误

忘记返回结果

router.beforeEach(function (to) {
  if (to.meta.requiresAuth) {
    console.log("需要登录");
  }
});

这种写法没有明确返回结果。更清晰的写法是根据条件返回地址或 true

router.beforeEach(function (to) {
  if (to.meta.requiresAuth && !isLogin) {
    return "/login";
  }
 
  return true;
});

登录页无限重定向

如果所有页面都无条件跳转到 /login,访问登录页时仍然会再次跳转到 /login。应该只在目标路由需要登录时进行判断:

if (to.meta.requiresAuth && !isLogin) {
  return "/login";
}

同时使用旧式 next

Vue Router 4 推荐直接通过返回值控制跳转。不要在同一个守卫中既使用 next(),又使用返回值:

router.beforeEach(function (to) {
  if (to.meta.requiresAuth && !isLogin) {
    return "/login";
  }
 
  return true;
});

理解

可以把全局前置守卫理解成进入每个页面前都要经过的门卫。

用户准备进入页面时,to 是用户想去的地方,from 是用户当前所在的地方。守卫先检查用户是否满足条件:满足就返回 true 放行,不满足就返回登录页或其他页面。

meta 像是写在路由上的备注。它不直接改变页面,但可以告诉守卫“这个页面需要登录”或“这个页面的标题是什么”。这样路由配置负责描述页面,守卫负责统一处理规则,代码会更容易维护。

引出

全局前置守卫的判断逻辑主要依赖条件判断和函数回调,可以结合 JS 条件判断JS 函数 继续理解。

当页面权限不只是“是否登录”,还涉及角色和操作权限时,还需要思考:路由权限应该如何分级,以及前端判断和后端权限校验应该怎样配合?