基于vueuse -useAxios的二次封装

前言

使用hooks写法,省去日常请求中响应式变量的定义,提高开发效率。

1.安装vueuse以及对应插件

npm i @vueuse/core @vueuse/integrations

2.核心代码

import { useAxios } from "@vueuse/integrations/useAxios";
import axios from "axios";
import { ElMessage } from "element-plus";

// 创建 axios 实例
const instance = axios.create({
  baseURL: "/api", // 基础 URL
  timeout: 10000, // 请求超时时间(10秒),可根据需要调整
  headers: {
    "Content-Type": "application/json;charset=utf-8", // 默认 Content-Type
  },
});

// 获取本地存储的 token
const getToken = () => localStorage.getItem("token");

// 请求拦截器:动态添加 Authorization 头
instance.interceptors.request.use(
  (config) => {
    const token = getToken();
    const isTokenDisabled = config.headers?.isToken === false; // 是否禁用 token
    if (token && !isTokenDisabled) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    // 请求发送前的错误处理
    return Promise.reject(error);
  }
);

// 业务状态码映射表(根据后端实际返回的 code 定义)
const businessStatusMap = {
  400: "错误请求",
  401: "未授权,请重新登录",
  403: "拒绝访问",
  404: "请求资源未找到",
  500: "服务器错误",
  503: "服务不可用",
  // 可根据实际业务扩展
};

/**
 * 封装的 HTTP 请求函数
 * @param {Object} config 请求配置
 * @param {string} config.url 请求的 URL(相对路径)
 * @param {string} [config.method="GET"] 请求方法,默认为 GET
 * @param {Object} [config.data] 请求体数据
 * @param {Object} [config.headers] 自定义请求头
 * @param {boolean} [config.immediate=false] 是否立即发起请求,默认为 false
 * @returns 返回 useAxios 的响应对象
 */
export default function useHttp(config = {}) {
  // 默认配置
  const defaultConfig = {
    method: "GET", // 默认请求方法
    immediate: false, // 默认立即发起请求
    ...config, // 合并用户传入的配置
  };
  const { url, immediate } = defaultConfig;
  // 使用 useAxios 发送请求
  const {
    data, // 响应数据
    isLoading, // 是否正在加载
    isFinished, // 请求是否完成
    error, // 错误对象
    execute, // 手动触发请求的函数
    abort, // 取消请求的函数
    isAborted, // 是否已取消
  } = useAxios(
    url,
    defaultConfig, // 合并后的配置
    instance,
    {
      immediate, // 是否立即执行
      onSuccess: (res) => {
        // 处理业务逻辑
        const code = res.code; // 假设后端返回的业务状态码在 res.code 中
        if (code !== 200) {
          const message = businessStatusMap[code] || res.msg || "业务请求失败";
          ElMessage.error(message);
          throw new Error(message); // 抛出错误,方便调用方捕获
        }
      },
      onError: (err) => {
        // 网络层错误已在拦截器中处理,这里只记录详细日志
        console.error("Request failed:", {
          url,
          error: err.response || err.message,
        });
      },
    }
  );
  return {
    data,
    isLoading,
    isFinished,
    error,
    execute,
    abort,
    isAborted,
  };
}

 

//@/api/login.js
import useHttp from "@/utils/request";
export const login = (data) => {
  return useHttp({
    url: "/login",
    method: "POST",
    data: data,
    headers: {
      isToken: false,
    },
  });
};

3.使用

<template>
  <div>
    <el-button type="primary" :loading="isLoading" @click="handleLogin">
      登录
    </el-button>
    <el-button type="danger" :loading="listLoading" @click="getData">
      列表
    </el-button>
    <div>{{ listCode }} - {{ listMsg }}</div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from "vue";
import { login, getList } from "./api/login";
import { ElMessage } from "element-plus";

// 登录请求
const { data, isLoading, execute } = login({
  username: "admin",
  password: "admin123",
});

// 列表请求
const {
  data: listData,
  isLoading: listLoading,
  execute: listExecute,
} = getList({
  pageSize: 10,
  pageNum: 1,
});

// 解包 listData 用于模板显示
const listCode = computed(() => listData.value?.code);
const listMsg = computed(() => listData.value?.msg);

const handleLogin = async () => {
  try {
    await execute();
    if (data.value.code === 200) {
      localStorage.setItem("token", data.value.token);
      ElMessage.success("登录成功");
      getData(); // 登录成功后获取列表
    } else {
      ElMessage.error(data.value.msg || "登录失败");
    }
  } catch (err) {
    ElMessage.error(err.message || "登录请求失败");
  }
};

const getData = async () => {
  try {
    await listExecute();
    if (listData.value.code === 200) {
      console.log("列表数据:", listData.value);
    } else {
      ElMessage.error(listData.value.msg || "获取列表失败");
    }
  } catch (err) {
    ElMessage.error(err.message || "列表请求失败");
  }
};
</script>

<style lang="scss" scoped></style>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值