确保刷新页面后用户登录状态不会失效,永久化存储用户登录信息
为了解决刷新页面后用户登录失效的问题,我们需要对以下三个文件进行修改:
-
main.js
:在应用初始化时从localStorage
中恢复 Token。 -
App.vue
:监听路由变化,确保登录状态和动态路由加载正确。 -
permission.js
(路由守卫):优化路由守卫逻辑,确保 Token 和动态路由的正确处理。
1. 修改 main.js
在应用初始化时,从 localStorage
中恢复 Token,并更新到 Vuex 中。
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import SvgIcon from '@/icons';
import '@/router/permission.js'; // 引入路由守卫
import zhCn from 'element-plus/es/locale/lang/zh-cn'; // 国际化中文
import request from "@/util/request";
const app = createApp(App);
// 从 localStorage 中恢复 Token
const token = localStorage.getItem('token');
if (token) {
store.commit('SET_TOKEN', token); // 更新 Vuex 中的 Token
}
// 注册全局属性
app.config.globalProperties.$request = request; // 挂载请求方法
app.config.globalProperties.$baseUrl = process.env.VUE_APP_BASEURL; // 挂载基础 URL
// 使用插件
SvgIcon(app);
app.use(ElementPlus, {
locale: zhCn,
});
app.use(store);
app.use(router);
// 挂载应用
app.mount('#app');
修改 App.vue
在 App.vue
中监听路由变化,确保登录状态和动态路由加载正确。
<template>
<router-view />
</template>
<script setup>
import { watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import store from './store';
const route = useRoute();
const whitePath = ['/adminlogin', '/index', '/'];
// 监听路由变化,动态添加标签页
watch(
route,
(to) => {
if (whitePath.indexOf(to.path) === -1) {
const tab = {
name: to.name,
path: to.path
};
store.commit("ADD_TABS", tab); // 添加标签页
}
},
{ deep: true, immediate: true }
);
// 在组件挂载时检查 Token
onMounted(() => {
const token = localStorage.getItem('token');
if (token) {
store.commit('SET_TOKEN', token); // 更新 Vuex 中的 Token
}
});
</script>
<style>
/* 全局样式 */
</style>
修改 permission.js
(路由守卫)
优化路由守卫逻辑,确保 Token 和动态路由的正确处理。
import router from "@/router";
import store from "@/store";
router.beforeEach(async (to, from, next) => {
console.log('Navigating from', from.path, 'to', to.path);
// 白名单路径(无需登录即可访问)
const whiteList = ['/adminlogin', '/login', '/register', '/forgetpassword'];
// 从 localStorage 中获取 Token
const token = localStorage.getItem('token') || store.getters.GET_TOKEN;
const hasRoutes = store.state.hasRoutes;
const menuList = store.getters.GET_MENULIST;
// 设置页面标题
document.title = store.getters.GET_WEBNAME;
if (token) {
// 有 Token 的情况
if (!hasRoutes) {
// 如果动态路由未加载,加载动态路由
try {
await bindRoute(menuList); // 动态加载路由
store.commit("SET_ROUTES_STATE", true); // 标记路由已加载
next({ ...to, replace: true }); // 重新跳转到目标路由
} catch (error) {
console.error('动态路由加载失败:', error);
next('/login'); // 如果加载失败,跳转到登录页
}
} else {
// 动态路由已加载,直接放行
next();
}
} else {
// 无 Token 的情况
if (whiteList.includes(to.path)) {
// 如果目标路径在白名单中,直接放行
next();
} else {
// 否则跳转到登录页
next("/login");
}
}
});
// 动态绑定路由
const bindRoute = (menuList) => {
return new Promise((resolve) => {
let newRoutes = router.options.routes;
menuList.forEach(menu => {
if (menu.children) {
menu.children.forEach(m => {
// 将菜单转换为路由
let route = menuToRoute(m, menu.name);
if (route) {
newRoutes[0].children.push(route); // 添加到路由管理
}
});
}
});
// 重新添加到路由
newRoutes.forEach(route => {
router.addRoute(route);
});
resolve();
});
};
// 菜单转成路由
const menuToRoute = (menu, parentName) => {
if (!menu.component) {
return null;
} else {
let route = {
name: menu.name,
path: menu.path,
meta: {
parentName: parentName
}
};
route.component = () => import(`@/views${menu.component}.vue`); // 动态加载组件
return route;
}
};
修改 store
中的 Token 逻辑
确保在 store
中正确设置和清除 Token。
-
持久化存储:将
token
、menuList
、webName
和userInfo
存储在localStorage
中,而不是sessionStorage
,因为sessionStorage
在页面刷新后会丢失。 -
初始化状态:在
store
初始化时,从localStorage
中恢复状态。 -
退出登录逻辑:在
logout
操作中,清除localStorage
和sessionStorage
,并重置state
。
import router from '@/router';
import { createStore } from 'vuex';
export default createStore({
state: {
// 存储 token
token: localStorage.getItem('token') || "", // 从 localStorage 中恢复 token
userName: localStorage.getItem('userName') || "", // 从 localStorage 中恢复 userName
hasRoutes: false,
editableTabsValue: '/index',
webName: localStorage.getItem('webName') || "", // 从 localStorage 中恢复 webName
editableTabs: [
{
title: '首页',
name: '/index'
}
],
menuList: JSON.parse(localStorage.getItem('menuList')) || [] // 从 localStorage 中恢复 menuList
},
getters: {
GET_TOKEN(state) {
return state.token;
},
GET_MENULIST(state) {
return state.menuList;
},
GET_WEBNAME(state) {
return state.webName;
},
GET_USERINFO(state) {
return {
username: state.userName
};
}
},
mutations: {
SET_TOKEN(state, token) {
state.token = token;
localStorage.setItem('token', token); // 存储到 localStorage
},
SET_MENULIST(state, menuList) {
state.menuList = menuList;
localStorage.setItem('menuList', JSON.stringify(menuList)); // 存储到 localStorage
},
SET_WEBNAME(state, webName) {
state.webName = webName;
localStorage.setItem('webName', webName); // 存储到 localStorage
},
SET_USERINFO(state, userInfo) {
state.userName = userInfo.username;
localStorage.setItem('userName', userInfo.username); // 存储到 localStorage
},
SET_ROUTES_STATE(state, hasRoutes) {
state.hasRoutes = hasRoutes;
},
ADD_TABS(state, tab) {
if (state.editableTabs.findIndex(e => e.name === tab.path) === -1) {
if (tab.path !== '/login' && tab.path !== '/register' && tab.path !== '/forgetpassword') {
state.editableTabs.push({
title: tab.name,
name: tab.path
});
}
}
state.editableTabsValue = tab.path;
},
RESET_TABS(state) {
state.editableTabsValue = '/index';
state.editableTabs = [
{
title: '首页',
name: '/index'
}
];
},
// 定义重置 state 的 mutation 函数
RESET_STATE(state) {
// 重置各个属性值
state.token = "";
state.userName = "";
state.hasRoutes = false;
state.editableTabsValue = '/index';
state.editableTabs = [
{
title: '首页',
name: '/index'
}
];
state.menuList = [];
// 清除 localStorage
localStorage.removeItem('token');
localStorage.removeItem('userName');
localStorage.removeItem('webName');
localStorage.removeItem('menuList');
}
},
actions: {
// 安全退出
logout({ commit }) {
// 调用 mutation 函数来重置 state
commit('RESET_STATE');
// 清除 sessionStorage
sessionStorage.clear();
// 跳转到登录页
router.replace("/login");
// 强制刷新页面以确保所有状态被清除
window.location.reload();
}
},
modules: {
}
});
-
持久化存储:
-
将
token
、userName
、webName
和menuList
存储在localStorage
中,确保刷新页面后数据不会丢失。 -
在
state
初始化时,从localStorage
中恢复数据。
-
-
退出登录逻辑:
-
在
logout
操作中,调用RESET_STATE
重置state
,并清除localStorage
和sessionStorage
。 -
使用
router.replace("/login")
跳转到登录页,并强制刷新页面以确保所有状态被清除。
-
-
状态恢复:
-
在
state
初始化时,从localStorage
中恢复token
、userName
、webName
和menuList
,确保刷新页面后状态不会丢失。
-