vue axios和全局loading关系处理

61 阅读2分钟

loading的展示和取消可以说是每个前端对接口的时候都要关心的一个问题。比如说table加载数据的时候,我希望他得是数据拉取完成,spin自动消失,那么这种我可以在页面定义一个loading ref。

const loading = ref(false)
const data = ref(null)
axios.post(`/api/data`).then((res) => {
    loading.value = false;
    data.value = res;
}).catch(error => {  
    console.log("失败:", error);  
}).finally(() => {  
    console.log("Promise 执行完毕,无论成功或失败。");  
    loading.value = false;
});

这样去处理loading,但是如果这样我再每个页面里面都需要这么去写loading,那么我就在想我能不能全局监听到axios是否正在调用api呢,想了下可以使用拦截器和结合pinia使用及时去处理当前访问网络的状态。

import { ref } from 'vue'
import { defineStore } from 'pinia'

//处理全局httploading
export const useHttpLoadingStore = defineStore('http-loading', () => {
  const isLoading = ref(false)

  function setLoading(loading: boolean) {
    if (isLoading.value === loading) return
    isLoading.value = loading
  }

  return { isLoading, setLoading }
})

在调用api的request里面

import axios from 'axios'
import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
import { useLiveUserStore } from '@/stores/user'
import { useHttpLoadingStore } from '@/stores/http-loading'

// 通用响应列表接口
export interface ResponseList<T = unknown> {
  count: number
  rows: T[]
}

// 创建axios实例
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 15000
})

// 请求拦截器
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    const httpLoadingStore = useHttpLoadingStore()
    httpLoadingStore.setLoading(true)
    // 从 liveUser store 获取 token 并添加到请求头
    const liveUser = useLiveUserStore()
    if (liveUser.token && config.headers) {
      config.headers['Authorization'] = `Bearer ${liveUser.token}`
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const res = response.data
    const httpLoadingStore = useHttpLoadingStore()
    httpLoadingStore.setLoading(false)
    // 根据后端返回的状态码处理不同情况
    if (response.status !== 200) {
      ElMessage.error(res.message || '请求错误')

      // 处理401未授权状态
      if (response.status === 401) {
        // 使用 liveUser store 的 logout 方法清除用户信息并跳转到登录页
        const liveUser = useLiveUserStore()
        liveUser.logout()
        router.push('/login')
      }

      return Promise.reject(new Error(res.error || '请求错误'))
    } else {
      return res
    }
  },
  (error) => {
    console.log(error);
    const httpLoadingStore = useHttpLoadingStore()
    httpLoadingStore.setLoading(false)
    // 处理HTTP错误状态
    if (error.response) {
      const { status } = error.response
      let message = '请求错误'
      if (error.response.data?.msg) {
        message = `${message}: ${error.response.data.msg}`
      }
      const liveUser = useLiveUserStore()

      // 处理常见的HTTP状态码
      if (status === 401) {
        message = '未授权,请重新登录'
        liveUser.logout()
        router.push('/login')
      } else if (status === 403) {
        message = '拒绝访问'
      } else if (status === 404) {
        message = '请求的资源不存在'
      } else if (status === 500) {
        message = '服务器内部错误'
      }

      ElMessage.error(message)
    } else {
      ElMessage.error('网络连接失败,请检查网络')
    }
    return Promise.reject(error)
  }
)

// 封装GET请求
export function requestGet<T = unknown>(url: string, params?: Record<string, unknown>): Promise<T> {
  return service.get(url, { params })
}

// 封装POST请求
export function requestPost<T = unknown>(url: string, data?: Record<string, unknown>): Promise<T> {
  return service.post(url, data)
}

// 封装PUT请求
export function requestPut<T = unknown>(url: string, data?: Record<string, unknown>): Promise<T> {
  return service.put(url, data)
}

// 封装DELETE请求
export function requestDel<T = unknown>(url: string, params?: Record<string, unknown>): Promise<T> {
  return service.delete(url, { params })
}

export default service

在页面具体使用的时候 比如el-button,直接加上就可以使用了。

<template>
    <el-button type="default" v-back v-loading="httpLoadingStore.isLoading">返回</el-button>
</template>

<script setup lang="ts">
import { useHttpLoadingStore } from '@/stores/http-loading';
const httpLoadingStore = useHttpLoadingStore()
</script>