第二十七节:进阶:路由权限控制 + 404 / 401 全局异常页面开发
2026/9/6 2:19:41 网站建设 项目流程

第二十七节:进阶:路由权限控制 + 404 / 401 全局异常页面开发

🎯本节目标

  1. 开发 404 页面(页面不存在)、401 无权限页面
  2. 完善路由守卫,实现路由权限校验
  3. 区分:登录校验、角色 / 权限校验、不存在页面跳转逻辑
  4. 处理白名单路由(登录页不用登录也能访问)
  5. 模拟无权限访问页面,跳转 401;访问不存在路由跳转 404

前面v‑perm控制按钮显示隐藏;路由权限控制整个页面能不能进

步骤 1:新建两个异常页面

① src/views/error/404.vue

<template> <div class="error-page"> <div class="code">404</div> <div class="desc">抱歉,你访问的页面不存在</div> <el-button type="primary" @click="$router.push('/dashboard')">返回工作台</el-button> </div> </template> <script setup></script> <style scoped> .error-page{ height: 100%; display:flex; flex-direction:column; align-items:center; justify-content:center; } .code{ font-size:80px; font-weight:bold; color:#909399; } .desc{ font-size:16px; color:#666; margin:16px 0 24px; } </style>

② src/views/error/401.vue

<template> <div class="error-page"> <div class="code">401</div> <div class="desc">抱歉,你没有权限访问该页面</div> <el-button type="primary" @click="$router.push('/dashboard')">返回工作台</el-button> </div> </template> <script setup></script> <style scoped> .error-page{ height: 100%; display:flex; flex-direction:column; align-items:center; justify-content:center; } .code{ font-size:80px; font-weight:bold; color:#E6A23C; } .desc{ font-size:16px; color:#666; margin:16px 0 24px; } </style>

步骤 2:配置路由 src/router/index.js

import { createRouter, createWebHistory } from 'vue-router' import Layout from '@/layout/index.vue' // 常量路由:所有用户都可以访问,不需要权限 export const constantRoutes = [ { path: '/login', component: () => import('@/views/login/index.vue'), meta: { title: '登录' } }, { path: '/', component: Layout, redirect: '/dashboard', children: [ { path: '/dashboard', component: () => import('@/views/dashboard/index.vue'), meta: { title: '工作台', icon: 'DataBoard' } } ] }, // 无权限页面 { path: '/401', component: () => import('@/views/error/401.vue') }, // 404页面,必须放在路由数组最后! { path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') } ] // 需要权限的业务路由(后续动态追加) export const asyncRoutes = [ { path: '/system', component: Layout, meta: { title: '系统管理', icon: 'Setting' }, children: [ { path: 'user', component: () => import('@/views/system/user/index.vue'), meta: { title: '用户管理', icon: 'User' } } ] } ] const router = createRouter({ history: createWebHistory(), routes: constantRoutes }) export default router

步骤 3:编写路由守卫 src/permission.js

新建src/permission.js,全局路由拦截逻辑

import router from '@/router' import { useUserStore } from '@/stores/user' // 白名单:不需要登录就可以访问的页面 const whiteList = ['/login','/401','/404'] router.beforeEach(async(to, from, next) => { const userStore = useUserStore() const token = userStore.token // 1、有token,已经登录 if(token){ if(to.path === '/login'){ // 已经登录,访问登录页,直接跳工作台 next('/dashboard') }else{ // 判断是否已经获取用户信息 if(!userStore.username){ try { // 这里可以调用接口获取用户信息,存入pinia // await getUserInfoApi() // 把动态权限路由追加到路由表 // router.addRoute(asyncRoutes) next({ ...to, replace:true }) }catch(err){ // token失效,清空状态回到登录 userStore.logout() next('/login') } }else{ // 已拿到用户信息,放行 next() } } }else{ // 2、没有token,未登录 if(whiteList.includes(to.path)){ // 在白名单,直接放行 next() }else{ // 不在白名单,跳登录页 next('/login') } } })

步骤 4:在 main.js 引入 permission,让守卫生效

import { createApp } from 'vue' import App from './App.vue' import '@/permission' // ✅导入路由守卫,必须写在createApp之前 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import { perm } from '@/directive/perm' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.directive('perm', perm) app.use(ElementPlus) app.mount('#app')

步骤 5:动态路由追加(重点:根据权限动态挂载路由)

现在所有路由全部写死,真正后台项目:登录拿到用户权限,再调用router.addRoute把有权限的路由注册进去。

修改src/permission.js,演示动态路由逻辑

import router from '@/router' import { useUserStore } from '@/stores/user' import { asyncRoutes } from '@/router' const whiteList = ['/login','/401','/404'] // 模拟过滤路由:根据用户权限过滤出可以访问的路由 function filterAsyncRoutes(routes, permissions){ const res = [] routes.forEach(route => { const temp = { ...route } // 简单演示:如果是admin直接全部放行 res.push(temp) }) return res } router.beforeEach(async(to, from, next) => { const userStore = useUserStore() const token = userStore.token if(token){ if(to.path === '/login'){ next('/dashboard') }else{ if(!userStore.username){ try { // 模拟登录后获取用户信息,这里是mock已经存好 // 根据权限过滤动态路由 const accessRoutes = filterAsyncRoutes(asyncRoutes, userStore.permissions) // 将过滤后的路由添加进路由实例 accessRoutes.forEach(item=>{ router.addRoute(item) }) // addRoute之后必须带上replace:true,防止路由404 next({ ...to, replace:true }) }catch(err){ userStore.logout() next('/login') } }else{ next() } } }else{ if(whiteList.includes(to.path)){ next() }else{ next('/login') } } })

✅测试验证清单

  1. 未登录状态:直接访问/dashboard/system/user,自动跳转到登录页面
  2. 访问不存在地址:例如http://localhost:5173/xxxabc,跳转到 404 页面
  3. 访问 /401:正常渲染无权限页面
  4. 登录成功:动态路由/system/user被 addRoute 注册,侧边栏自动渲染系统管理菜单
  5. 已经登录后手动访问/login,自动跳转到工作台

关键知识点

  1. /:pathMatch(.*)*404 路由必须放在路由数组最后;如果写在前面,所有页面都会命中 404。
  2. router.addRoute()添加动态路由之后,next({...to,replace:true})必不可少,否则新增路由会报 404。
  3. 白名单路由,不需要 token 也能进入。
  4. 前端路由权限只是拦截跳转,接口层面依旧需要后端做权限校验

踩坑点

  1. @/permission需要在 main.js 尽早导入,要在createApp(App)之前引入,守卫才生效。
  2. 刷新页面 pinia 状态丢失,搭配 pinia 持久化插件,token、用户信息刷新页面不丢失。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询