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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:48:19