在VueJS应用中添加全局状态实现初始化加载屏的开发问题
实现步骤
1. 修正枚举定义
原枚举缺少INIT状态,补全后代码如下:
// enums.js export default { INIT: -1, LOADING: 0, LOADED: 1 };
2. 完善Vuex相关逻辑
2.1 修正auth模块请求返回
auth.js中getUserData方法当前没有返回Promise,导致无法判断请求完成状态,修改为:
// auth.js 对应actions内的getUserData getUserData ({ commit }) { return axios .get('user') .then(response => { commit('setUserData', response.data) }) .catch(() => { localStorage.removeItem('authToken') }) },
2.2 新增全局初始化Action
修改store/index.js,新增全局初始化逻辑、补充对应getter:
// store/index.js 完整修改后代码 import Vue from 'vue' import Vuex from 'vuex' import auth from './auth' import appdata from './appData.js' import ENUM from '../enums'; Vue.use(Vuex) export default new Vuex.Store({ state: { backEndErrors: [], apiState: ENUM.INIT }, getters: { backEndErrors: state => state.backEndErrors, apiState: state => state.apiState, // 直接对外暴露加载完成状态,方便组件调用 isAppLoaded: state => state.apiState === ENUM.LOADED }, mutations: { setErrors (state, errors) { state.backEndErrors = errors }, setApiState (state, apiState) { state.apiState = apiState; }, }, actions: { // 全局初始化Action,统一处理所有启动请求 async initApp({ commit, dispatch }) { commit('setApiState', ENUM.LOADING) const initRequests = [] // 有登录凭证的情况下才请求用户信息 if (localStorage.getItem('authToken')) { initRequests.push(dispatch('auth/getUserData')) } // 全局基础数据请求 initRequests.push(dispatch('appdata/getStatusesData')) // 等待所有请求完成(成功/失败都算完成,避免某个请求失败导致卡加载) await Promise.allSettled(initRequests) commit('setApiState', ENUM.LOADED) } }, modules: { auth: auth, appdata: appdata } })
3. 修改App.vue逻辑
3.1 模板新增加载屏
修改template部分,添加加载屏判断,只有初始化完成后才显示主应用内容:
<template> <div id="app"> <!-- 加载屏 --> <div v-if="!isAppLoaded" class="loading-wrapper"> <div class="spinning-loader"></div> </div> <!-- 主应用内容 --> <template v-else> <Sidebar v-if="isLogged" :class="{ 'show-sidebar': !isSmallSideBar, 'sidebar-hidden' : !isLogged }" @sideBarGrow="isSmallSideBar = false" @sideBarShrink="isSmallSideBar = true" @logout="logout" /> <DynamicHeader :class="{ 'margin-header' : !isSmallSideBar }" @toogleSideBar="isSmallSideBar = !isSmallSideBar" /> <div class="content" :class="{ 'margin-content' : !isSmallSideBar }"> <router-view></router-view> </div> </template> </div> </template>
3.2 调整生命周期逻辑
修改script部分,移除mounted中原有的分散请求,替换为调用全局初始化Action,补充状态映射:
<script> import { mapGetters, mapActions } from 'vuex' import Sidebar from './components/partials/Sidebar/Sidebar.vue' import DynamicHeader from './components/partials/header/DynamicHeader.vue' export default { data: function () { return { isSmallSideBar: true } }, components: { Sidebar, DynamicHeader }, async mounted () { // 执行全局初始化 await this.initApp() // 初始化完成后如果未登录,跳转到登录页 if (!this.isLogged) { this.$router.push('/login').catch() } }, computed: { ...mapGetters(['isAppLoaded']), ...mapGetters('auth', ['user', 'isLogged']), ...mapGetters('appdata', ['statuses']) }, methods: { ...mapActions(['initApp']), ...mapActions('auth', ['sendLogoutRequest', 'getUserData']), ...mapActions('appdata', ['getStatusesData']), logout () { this.sendLogoutRequest().then(() => { this.$router.push('/login').catch() }) }, } } </script>
3.3 补充加载屏样式
在style部分新增加载屏容器样式,实现全屏居中效果:
<style> /* 原有样式保留,新增以下代码 */ .loading-wrapper { width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #ffffff; } /* 原有样式保留 */ </style>
内容的提问来源于stack exchange,提问作者Kaloyan Nikolov
相关产品推荐
相关产品推荐

