You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()函数中定义的初始值,这就是所谓“遗忘”状态的本质。

除此之外,当前代码还有两个细节问题:

  1. 登录状态校验时机滞后:组件挂载时先打印状态,再调用checkUserLogged,首次打印自然是初始空状态;
  2. 页面内跳转未同步状态:从/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 02:59:51