Vuex状态丢失问题:页面刷新后状态重置排查
问题描述
我正在开发一个带登录功能的Vue应用,现有三个路由:
- "/":未登录用户可访问的着陆页,对应组件
TheIndex.vue - "/dashboard":登录后进入的首页,对应组件
TheDashboard.vue - "/dashboard/editsets":从
/dashboard跳转的页面,对应组件TheEditsets.vue
应用从数据库获取主题数据,除"/"外的路由仅允许登录用户访问,路由拦截功能正常。但Vuex中存储的统计数据会丢失,导致后续交互异常。
监控状态时发现:
- 在
TheDashboard.vue的beforeMount()中,登录后首次打印$store.state,LOG_INFO_islogged为true但其他属性为空; - 刷新页面后,首次打印的
LOG_INFO_islogged为false且无用户数据,尽管之前已设置过状态; - 子组件因状态为空或
null无法正常获取用户数据。
相关代码片段
TheDashboard.vue 的 beforeMount 钩子
async beforeMount() { console.log(this.$store.state); let initial_logincheck = await this.$store.dispatch("checkUserLogged"); if(initial_logincheck.success) { this.$store.commit("login", initial_logincheck.logged_user); this.loggedUserName = initial_logincheck.logged_user; } else { console.error("USER NICHT EINGELOGGT!"); } if(this.$store.getters.getLoggedState) { await this.getTotals(); } else { this.$router.push("/"); } console.log(this.$store.state); },
main.js 中的 Vuex 配置
//VUE-X import {createStore} from "vuex"; const store = createStore({ state() { return { API : "http://localhost/vue_projects/vocabtrainer/backend/main.php", LOG_INFO_islogged : false, LOG_INFO_name : null, USER_INFO_totaltopics : null, USER_INFO_totalvocabs : null, USER_topicArray : [], USER_tIdx : 0, USER_vIdx : 0, USER_vocabArray : [], }; }, mutations: { logout(state) { state.LOG_INFO_islogged = false; state.LOG_INFO_name = null; state.USER_INFO_totaltopics = null; state.USER_INFO_totalvocabs = null; router.push("/"); }, login(state, username) { state.LOG_INFO_islogged = true; state.LOG_INFO_name = username; router.push("/dashboard"); }, setTotals(state, result) { state.USER_INFO_totaltopics = result.totalTopics; state.USER_INFO_totalvocabs = result.totalVocabs; }, setTopicStats(state, fetchedTopics) { state.USER_topicArray = [...fetchedTopics.topics]; }, clearTopicStats(state) { state.USER_topicArray.length = 0; state.USER_vocabArray.length = 0; state.USER_tIdx = 0; state.USER_vIdx = 0; }, }, actions: { async post(context, dataobject) { [...] }, async checkUserLogged() { let checkloggedobject = {task: "is_a_user_logged"}; let checkloggedresult = await this.dispatch("post", checkloggedobject); return checkloggedresult; }, }, getters: { tIndex(state) { return state.USER_tIdx; }, getTopicArray(state) { return state.USER_topicArray; }, getLoggedState(state) { return state.LOG_INFO_islogged; }, getLoggedUsernameState(state) { return state.LOG_INFO_name; }, getTopicsCountState(state) { return state.USER_INFO_totaltopics; }, getVocabsCountState(state) { return state.USER_INFO_totalvocabs; }, }, });
TheDashboard.vue 完整 script 部分
data() { return { loggedUserName : "", totalTopics : null, totalVocabs : null, }; }, async beforeMount() { console.log(this.$store.state); let initial_logincheck = await this.$store.dispatch("checkUserLogged"); if(initial_logincheck.success) { this.$store.commit("login", initial_logincheck.logged_user); this.loggedUserName = initial_logincheck.logged_user; } else { console.error("USER NICHT EINGELOGGT!"); } if(this.$store.getters.getLoggedState) { await this.getTotals(); } else { this.$router.push("/"); } console.log(this.$store.state); }, methods: { async getTotals() { let totalsrequest = {task: "get_totals"}; let totalresult = await this.$store.dispatch("post", totalsrequest); if(totalresult.success) { this.$store.commit("setTotals", totalresult); this.totalTopics = totalresult.totalTopics; this.totalVocabs = totalresult.totalVocabs; } else { console.error("TOTALS NICHT GEHOLT!"); } }, async logout() { [...] }, }, };
TheEditsets.vue 的 script 部分
data() { return { // .name, .totalVocabs, .level topicArray : [], tIdx : 0, vocabArray : [], }; }, computed: { getTotalTopics() { return this.$store.state.USER_INFO_totaltopics; } }, async beforeMount() { console.clear(); console.log(this.$store.state); let initial_logincheck = await this.$store.dispatch("checkUserLogged"); if(!initial_logincheck.success) { this.$router.push("/"); return; } let allStats = await this.$store.dispatch("post", {task: "fetch_all_topic_stats"}); this.$store.commit("setTopicStats", allStats); console.log(this.$store.state); } };
请问为何Vuex会“遗忘”我设置的状态值?
原因分析与解决方案
核心原因
Vuex的状态仅存储在浏览器内存中,当页面刷新时,整个Vue应用会重新初始化,Vuex状态会被重置为state()函数中定义的初始值,这就是所谓“遗忘”状态的本质。
除此之外,当前代码还有两个细节问题:
- 登录状态校验时机滞后:组件挂载时先打印状态,再调用
checkUserLogged,首次打印自然是初始空状态; - 页面内跳转未同步状态:从
/dashboard跳转到/dashboard/editsets时,TheEditsets.vue只做了登录校验,但没有同步获取USER_INFO_totaltopics等统计数据,导致依赖这些状态的计算属性无法正常工作。
解决方案
1. 持久化Vuex状态(解决刷新丢失问题)
将Vuex状态同步到localStorage或sessionStorage,页面初始化时从存储中恢复状态:
// main.js 中 Vuex 配置 const store = createStore({ state() { // 从localStorage恢复状态 const savedState = localStorage.getItem('vuexState'); return savedState ? JSON.parse(savedState) : { API : "http://localhost/vue_projects/vocabtrainer/backend/main.php", LOG_INFO_islogged : false, LOG_INFO_name : null, USER_INFO_totaltopics : null, USER_INFO_totalvocabs : null, USER_topicArray : [], USER_tIdx : 0, USER_vIdx : 0, USER_vocabArray : [], }; }, mutations: { logout(state) { state.LOG_INFO_islogged = false; state.LOG_INFO_name = null; state.USER_INFO_totaltopics = null; state.USER_INFO_totalvocabs = null; // 同步到localStorage localStorage.setItem('vuexState', JSON.stringify(state)); router.push("/"); }, login(state, username) { state.LOG_INFO_islogged = true; state.LOG_INFO_name = username; localStorage.setItem('vuexState', JSON.stringify(state)); router.push("/dashboard"); }, setTotals(state, result) { state.USER_INFO_totaltopics = result.totalTopics; state.USER_INFO_totalvocabs = result.totalVocabs; localStorage.setItem('vuexState', JSON.stringify(state)); }, // 其他mutation同理,修改状态后同步存储 }, // ... 其余配置不变 });
2. 全局路由守卫统一处理登录校验
避免每个组件重复写checkUserLogged,在路由进入前完成校验和状态初始化:
// main.js 中添加路由守卫 router.beforeEach(async (to, from, next) => { if (to.path !== '/' && !store.state.LOG_INFO_islogged) { const loginCheck = await store.dispatch("checkUserLogged"); if (loginCheck.success) { store.commit("login", loginCheck.logged_user); // 同步获取统计数据 await store.dispatch("post", {task: "get_totals"}).then(res => { if(res.success) store.commit("setTotals", res); }); next(); } else { next('/'); } } else { next(); } });
3. 组件内状态依赖优化
在TheEditsets.vue中,若需要USER_INFO_totaltopics,可通过getters获取,并在状态为空时触发数据请求:
computed: { getTotalTopics() { return this.$store.getters.getTopicsCountState; } }, async beforeMount() { if (!this.getTotalTopics) { // 若状态为空,重新请求统计数据 const totalResult = await this.$store.dispatch("post", {task: "get_totals"}); if(totalResult.success) this.$store.commit("setTotals", totalResult); } // ... 其余逻辑不变 }
内容的提问来源于stack exchange,提问作者Jericho
相关产品推荐
相关产品推荐

