v-skeleton-loader结合Vuex实现加载状态的最佳实践
原代码失效原因
- 违反Vuex状态修改规则:action中直接操作
state.loading = false,没有通过mutation提交状态变更,Vue响应式系统无法追踪到状态更新,视图不会同步刷新 - 状态重置逻辑缺失:发起新请求前没有将loading重置为
true,首次加载完成后后续再触发请求,loading一直是false,骨架屏不会显示 - 异常分支状态漏处理:请求进入catch分支时没有关闭loading,报错时骨架屏会一直处于加载状态
- 组件用法不规范:实际业务内容没有放在
v-skeleton-loader的默认插槽内,容易出现加载未完成时业务内容提前透出的问题
基于Vuex的正确实现
Store层修正
所有state变更必须通过mutation提交,请求发起前主动开启loading,请求结束(无论成功/失败)统一关闭loading:
// store.js import axios from 'axios' export default { state: () => ({ loading: false, tableData: null }), mutations: { SET_LOADING(state, status) { state.loading = status }, SET_TABLE_DATA(state, data) { state.tableData = data } }, actions: { async getTableData({ commit }, params) { // 请求发起前重置loading为true,自动触发骨架屏 commit('SET_LOADING', true) try { const res = await axios.get('/your/api/url', { params }) commit('SET_TABLE_DATA', res.data) } catch (err) { console.error('请求异常:', err) } finally { // 所有分支走完后关闭loading commit('SET_LOADING', false) } } } }
如果项目有多个独立页面的骨架屏场景,不要全局共用一个loading字段,按业务模块拆分Vuex模块,每个模块维护自身的loading状态,避免状态互相干扰。
组件层调用
通过计算属性映射Vuex状态,将业务内容放在骨架屏组件的插槽内,任意场景需要重新触发骨架屏,直接重新调用对应action即可,不需要手动重置loading:
<template> <v-skeleton-loader type="table" :loading="loading"> <!-- loading为false时自动展示插槽内的实际内容 --> <v-table :items="tableData" v-if="tableData"> <!-- 表格列定义 --> </v-table> </v-skeleton-loader> </template> <script> import { mapState } from 'vuex' export default { computed: { ...mapState(['loading', 'tableData']) }, mounted() { this.loadData() }, methods: { loadData(id) { this.$store.dispatch('getTableData', { id }) }, // 刷新、筛选等需要重新拉数的场景,直接调用loadData即可自动触发骨架屏 handleRefresh() { this.loadData() } } } </script>
不依赖Vuex的轻量实现
如果loading状态只在单个组件内使用,不需要全局共享,直接在组件内部维护状态即可,实现更简洁、维护成本更低:
<template> <v-skeleton-loader type="table" :loading="loading"> <v-table :items="tableData" v-if="tableData"> <!-- 表格内容 --> </v-table> </v-skeleton-loader> </template> <script> import axios from 'axios' export default { data() { return { loading: false, tableData: null } }, mounted() { this.fetchData() }, methods: { async fetchData() { this.loading = true try { const res = await axios.get('/your/api/url') this.tableData = res.data } catch (err) { console.error(err) } finally { this.loading = false } }, handleRefresh() { this.fetchData() } } } </script>
扩展注意事项
- 并行多请求场景:用计数器维护loading状态,每发起一个请求计数器+1,每完成一个请求计数器-1,计数器>0时loading为true,计数器归0时关闭loading,避免单个请求提前结束导致骨架屏消失
- 全局loading场景:可以封装统一的axios拦截器,自动维护全局loading状态,不需要每个接口单独写loading逻辑
- Vuex严格模式下必须通过mutation修改state,否则状态变更无法被响应式系统捕获,会出现视图不更新的问题
内容的提问来源于stack exchange,提问作者seary
相关产品推荐
相关产品推荐

