vue3+ts+elementplus的axios二次封装

430 阅读1分钟

1.创建 useAxios.ts 文件:

创建一个 TypeScript 版本的 useAxios.ts 文件,并添加类型注解。

// useAxios.ts

import { ref } from 'vue';
import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import router from './router'; // 导入 Vue Router

interface UseAxiosResponse<T> {
  loading: Ref<boolean>;
  error: Ref<Error | null>;
  data: Ref<T | null>;
  fetchData: (url: string, config?: AxiosRequestConfig) => Promise<void>;
}

export function useAxios<T = any>(): UseAxiosResponse<T> {
  const loading = ref(false);
  const error = ref<Error | null>(null);
  const data = ref<T | null>(null);

  const instance: AxiosInstance = axios.create({
    baseURL: 'https://api.example.com', // 根据您的 API 设置
    timeout: 5000, // 请求超时时间
  });

  instance.interceptors.request.use(
    (config: AxiosRequestConfig) => {
      loading.value = true;
      error.value = null;
      const token = localStorage.getItem('token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    },
    (error: Error) => {
      return Promise.reject(error);
    }
  );

  instance.interceptors.response.use(
    (response: AxiosResponse) => {
      loading.value = false;
      data.value = response.data;
      return response;
    },
    (error: Error) => {
      loading.value = false;
      error.value = error;
      
      if (error.response) {
        const status = error.response.status;
        if (status === 401) {
          ElMessageBox.confirm('登录已过期,请重新登录', '提示', {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            router.push('/login'); // 导航到登录页面
          }).catch(() => {
            // 用户点击取消按钮后的处理
          });
        } else if (status === 403) {
          ElMessage.error('无权限访问该资源');
        } else if (status === 500) {
          ElMessage.error('服务器出错,请稍后重试');
        } else if (status === 405) {
          ElMessage.error('请求方法不允许');
        }
      }
      
      return Promise.reject(error);
    }
  );

  const fetchData = async (url: string, config: AxiosRequestConfig = {}): Promise<void> => {
    loading.value = true;
    error.value = null;

    try {
      const response = await instance.get(url, config);
      data.value = response.data;
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  };

  return {
    loading,
    error,
    data,
    fetchData,
  };
}

2.创建hook 组件:

在 Vue 组件中,添加 TypeScript 类型注解,并使用 ref 函数定义响应式变量的类型

<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">{{ error.message }}</div>
    <div v-else>{{ data }}</div>
  </div>
</template>

<script lang="ts">
import { ref, onMounted, Ref } from 'vue';
import { useAxios } from '@/useAxios';

export default {
  setup() {
    const { loading, error, data, fetchData }: {
      loading: Ref<boolean>;
      error: Ref<Error | null>;
      data: Ref<any | null>;
      fetchData: (url: string) => Promise<void>;
    } = useAxios();

    // 在组件加载时立即发起接口请求
    onMounted(() => {
      fetchData('/api/data'); // 传入要请求的接口 URL
    });

    return {
      loading,
      error,
      data,
    };
  },
};
</script>