全局路由守卫
// main.js 或 router.js
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
if (to.meta.requiresAuth && !token) {
next({ path: '/login', query: { redirect: to.fullPath } })
} else {
next()
}
})
-
to.meta.requiresAuth
:判断目标路由是否需要权限 -
token
:模拟登录状态 -
next()
:控制跳转逻辑
路由配置中的 meta
字段
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
{
path: '/login',
component: Login
}
]
-
meta.requiresAuth: true
表示该路由需要登录权限
全局前置守卫 router.beforeEach
这是最常用的守卫,通常用于:
登录验证
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
if (to.meta.requiresAuth && !token) {
next({ path: '/login', query: { redirect: to.fullPath } })
} else {
next()
}
})
-
检查是否登录
-
如果未登录,跳转到登录页
-
登录后可重定向回原页面
权限控制
if (to.meta.role && !userHasRole(to.meta.role)) {
next('/403') // 无权限页面
}
-
根据
meta.role
判断用户是否有访问权限
页面标题设置
document.title = to.meta.title || '默认标题'
加载动画控制
startLoading() // 显示 loading 动画
next()
全局后置守卫 router.afterEach
用于处理跳转完成后的逻辑:
router.afterEach((to, from) => {
stopLoading() // 关闭 loading 动画
window.scrollTo(0, 0) // 页面回到顶部
})
一个简单的加载动画示例
1. 安装 Element UI(如果尚未安装)
npm install element-ui
2. 在 main.js
中引入 Element UI
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
3. 在 router.js
或 main.js
中添加路由守卫
import router from './router'
import { Loading } from 'element-ui'
let loadingInstance = null
router.beforeEach((to, from, next) => {
// 显示加载动画
loadingInstance = Loading.service({
lock: true,
text: '页面加载中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
})
next()
})
router.afterEach(() => {
// 关闭加载动画
if (loadingInstance) {
loadingInstance.close()
}
})
补充说明
-
Loading.service()
会创建一个全屏加载动画 -
loadingInstance.close()
用于关闭动画,确保不会残留 -
你可以根据路由
meta
字段决定是否显示动画(比如某些页面不需要)
自定义加载动画组件示例
1. 创建组件 LoadingOverlay.vue
<template>
<div v-if="visible" class="loading-overlay">
<div class="spinner"></div>
<p>页面加载中...</p>
</div>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false
}
}
}
</script>
<style scoped>
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.4);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
}
.spinner {
width: 50px;
height: 50px;
border: 6px solid #ccc;
border-top-color: #42b983;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
2. 在 Vuex 中添加 loading 状态(例如在 AppModule
)
// store/modules/AppModule.js
export default {
namespaced: true,
state: {
loading: false
},
mutations: {
setLoading(state, payload) {
state.loading = payload
}
}
}
3. 在 App.vue
中使用组件并绑定 Vuex 状态
<template>
<div id="app">
<router-view />
<LoadingOverlay :visible="loading" />
</div>
</template>
<script>
import LoadingOverlay from '@/components/LoadingOverlay.vue'
import { mapState } from 'vuex'
export default {
components: { LoadingOverlay },
computed: {
...mapState('AppModule', ['loading'])
}
}
</script>
4. 在路由守卫中控制 loading 状态
import store from '@/store'
router.beforeEach((to, from, next) => {
store.commit('AppModule/setLoading', true)
next()
})
router.afterEach(() => {
setTimeout(() => {
store.commit('AppModule/setLoading', false)
}, 500) // 延迟关闭,避免闪烁
})
效果说明
-
页面跳转时显示一个全屏遮罩和旋转动画
-
路由完成后自动关闭
-
可根据需要扩展为请求级 loading、模块级 loading 等
路由级守卫(单个路由)
用于某个路由单独控制
const router = new VueRouter({
routes: [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
const isAdmin = checkAdmin()
if (isAdmin) {
next()
} else {
next('/not-authorized')
}
}
}
]
})
-
beforeEnter
:只在进入该路由时触发
组件内守卫
export default {
name: 'Profile',
beforeRouteEnter(to, from, next) {
next(vm => {
vm.fetchUserData()// 组件实例还没创建,需通过 next(vm)
})
},
beforeRouteUpdate(to, from, next) {
this.fetchUserData()// 路由参数变化时重新加载数据
next()
},
beforeRouteLeave(to, from, next) {
const answer = window.confirm('确定要离开吗?')
if (answer) {
next()
} else {
next(false)
}
}
}
-
beforeRouteEnter
:组件创建前 -
beforeRouteUpdate
:路由参数变化时 -
beforeRouteLeave
:离开当前组件前
登录状态判断示例(结合 sessionStorage)
router.beforeEach((to, from, next) => {
const userId = sessionStorage.getItem('userId')
if (to.meta.requireAuth && !userId) {
next({ path: '/login', query: { redirect: to.fullPath } })
} else {
next()
}
})
结合 axios 请求拦截器(非路由,但常一起使用)
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = token
}
return config
})
axios.interceptors.response.use(response => {
if (response.data.code === 401) {
router.replace({ path: '/login' })
}
return response
})
常见用途总结
功能 | 守卫位置 | 示例 |
---|---|---|
登录验证 | beforeEach | 检查 token |
权限控制 | beforeEach / beforeEnter | 判断角色 |
页面标题 | beforeEach | 设置 document.title |
页面加载动画 | beforeEach / afterEach | 显示/关闭 loading |
数据初始化 | beforeRouteEnter / beforeRouteUpdate | 调用 API |
离开确认 | beforeRouteLeave | 弹窗确认 |