首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用 Vue 框架封装 Axios 解决网络请求常见问题的实践方法

使用 Vue 框架封装 Axios 解决网络请求常见问题的实践方法

作者头像
小焱
发布2025-05-21 17:46:37
发布2025-05-21 17:46:37
46500
代码可运行
举报
文章被收录于专栏:前端开发前端开发
运行总次数:0
代码可运行

Vue中封装Axios的技术方案与实践

一、Axios简介与Vue集成必要性

(一)Axios基本特性

Axios是一个基于Promise的HTTP客户端,专为浏览器和Node.js设计,具有以下特性:

  • 支持浏览器和Node.js
  • 支持Promise API
  • 能拦截请求和响应
  • 能转换请求数据和响应数据
  • 能取消请求
  • 自动转换JSON数据
  • 客户端支持防御XSRF

(二)Vue集成Axios的优势

在Vue项目中使用Axios可以带来以下优势:

  • 统一的API请求处理
  • 请求和响应拦截器实现全局处理
  • 错误处理统一化
  • 请求配置可复用
  • 支持TypeScript类型定义

二、基础封装方案

(一)安装与基本配置

代码语言:bash
复制
npm install axios
代码语言:javascript
代码运行次数:0
运行
复制
// src/utils/request.js
import axios from 'axios';

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // API基础URL
  timeout: 5000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在发送请求前做些什么
    // 例如添加token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    // 处理请求错误
    console.error('Request error:', error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 对响应数据做些什么
    const res = response.data;
    // 假设业务状态码0表示成功
    if (res.code !== 0) {
      console.error('Response error:', res.message);
      return Promise.reject(new Error(res.message || 'Error'));
    } else {
      return res.data;
    }
  },
  error => {
    // 处理响应错误
    console.error('Response error:', error);
    return Promise.reject(error);
  }
);

export default service;

(二)模块化API封装

代码语言:javascript
代码运行次数:0
运行
复制
// src/api/user.js
import request from '@/utils/request';

/**
 * 获取用户列表
 * @param {Object} params 查询参数
 * @returns {Promise}
 */
export function getUserList(params) {
  return request({
    url: '/api/users',
    method: 'get',
    params
  });
}

/**
 * 创建用户
 * @param {Object} data 用户数据
 * @returns {Promise}
 */
export function createUser(data) {
  return request({
    url: '/api/users',
    method: 'post',
    data
  });
}

/**
 * 获取用户详情
 * @param {string} id 用户ID
 * @returns {Promise}
 */
export function getUserDetail(id) {
  return request({
    url: `/api/users/${id}`,
    method: 'get'
  });
}

/**
 * 更新用户信息
 * @param {string} id 用户ID
 * @param {Object} data 用户数据
 * @returns {Promise}
 */
export function updateUser(id, data) {
  return request({
    url: `/api/users/${id}`,
    method: 'put',
    data
  });
}

/**
 * 删除用户
 * @param {string} id 用户ID
 * @returns {Promise}
 */
export function deleteUser(id) {
  return request({
    url: `/api/users/${id}`,
    method: 'delete'
  });
}

三、高级封装方案

(一)支持多种请求方式的封装

代码语言:javascript
代码运行次数:0
运行
复制
// src/utils/request.js
import axios from 'axios';

class Request {
  constructor(config) {
    this.service = axios.create(config);
    
    // 请求拦截器
    this.service.interceptors.request.use(
      config => {
        // 添加token
        const token = localStorage.getItem('token');
        if (token) {
          config.headers['Authorization'] = `Bearer ${token}`;
        }
        return config;
      },
      error => {
        console.error('Request error:', error);
        return Promise.reject(error);
      }
    );
    
    // 响应拦截器
    this.service.interceptors.response.use(
      response => {
        const res = response.data;
        if (res.code !== 0) {
          console.error('Response error:', res.message);
          return Promise.reject(new Error(res.message || 'Error'));
        } else {
          return res.data;
        }
      },
      error => {
        console.error('Response error:', error);
        return Promise.reject(error);
      }
    );
  }
  
  // 通用请求方法
  request(config) {
    return this.service(config);
  }
  
  // get请求
  get(url, config) {
    return this.service({ url, method: 'get', ...config });
  }
  
  // post请求
  post(url, data, config) {
    return this.service({ url, method: 'post', data, ...config });
  }
  
  // put请求
  put(url, data, config) {
    return this.service({ url, method: 'put', data, ...config });
  }
  
  // delete请求
  delete(url, config) {
    return this.service({ url, method: 'delete', ...config });
  }
}

// 创建实例
const request = new Request({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
});

export default request;

(二)错误处理增强

代码语言:javascript
代码运行次数:0
运行
复制
// src/utils/errorHandler.js
export function handleApiError(error) {
  let message = '未知错误';
  
  if (error.response) {
    // 请求已发送,服务器返回状态码不是2xx
    const { status, data } = error.response;
    
    switch (status) {
      case 400:
        message = data.message || '请求参数错误';
        break;
      case 401:
        message = '未授权,请登录';
        // 跳转到登录页
        window.location.href = '/login';
        break;
      case 403:
        message = '拒绝访问';
        break;
      case 404:
        message = '请求资源不存在';
        break;
      case 500:
        message = '服务器内部错误';
        break;
      default:
        message = `请求错误,状态码: ${status}`;
    }
  } else if (error.request) {
    // 请求已发送,但没有收到响应
    message = '网络连接超时';
  } else {
    // 发送请求时出错
    message = error.message;
  }
  
  // 显示错误提示
  console.error('API Error:', message);
  
  return Promise.reject(new Error(message));
}
代码语言:javascript
代码运行次数:0
运行
复制
// src/utils/request.js
// ...原有代码...

// 响应拦截器
this.service.interceptors.response.use(
  response => {
    const res = response.data;
    if (res.code !== 0) {
      return handleApiError({ response: { data: res } });
    } else {
      return res.data;
    }
  },
  error => {
    return handleApiError(error);
  }
);

// ...原有代码...

(三)请求取消功能

代码语言:javascript
代码运行次数:0
运行
复制
// src/utils/request.js
import axios from 'axios';

class Request {
  constructor(config) {
    this.service = axios.create(config);
    this.pendingRequests = new Map();
  }
  
  // ...原有代码...
  
  // 生成请求标识
  generateRequestKey(config) {
    const { method, url, params, data } = config;
    return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
  }
  
  // 添加请求到pending列表
  addPendingRequest(config) {
    const requestKey = this.generateRequestKey(config);
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
      if (!this.pendingRequests.has(requestKey)) {
        this.pendingRequests.set(requestKey, cancel);
      }
    });
  }
  
  // 移除请求
  removePendingRequest(config) {
    const requestKey = this.generateRequestKey(config);
    if (this.pendingRequests.has(requestKey)) {
      const cancel = this.pendingRequests.get(requestKey);
      cancel('Request canceled by the user.');
      this.pendingRequests.delete(requestKey);
    }
  }
  
  // 请求拦截器
  setupInterceptors() {
    // 请求拦截器
    this.service.interceptors.request.use(
      config => {
        // 取消重复请求
        this.removePendingRequest(config);
        this.addPendingRequest(config);
        
        // 添加token
        const token = localStorage.getItem('token');
        if (token) {
          config.headers['Authorization'] = `Bearer ${token}`;
        }
        
        return config;
      },
      error => {
        return Promise.reject(error);
      }
    );
    
    // 响应拦截器
    this.service.interceptors.response.use(
      response => {
        // 请求成功后从pending列表中移除
        this.removePendingRequest(response.config);
        return response.data;
      },
      error => {
        // 请求失败后从pending列表中移除
        if (axios.isCancel(error)) {
          console.log('Request canceled:', error.message);
        } else {
          this.removePendingRequest(error.config || {});
        }
        
        return handleApiError(error);
      }
    );
  }
}

四、Vue3组合式API封装示例

(一)使用组合式函数封装

代码语言:javascript
代码运行次数:0
运行
复制
// src/composables/useApi.js
import { ref, reactive, onMounted, watchEffect } from 'vue';
import request from '@/utils/request';

export function useApi(url, options = {}) {
  const data = ref(null);
  const loading = ref(false);
  const error = ref(null);
  
  const { 
    method = 'get', 
    immediate = true, 
    params = {}, 
    data: requestData = {} 
  } = options;
  
  const fetchData = async (overrideParams = {}) => {
    loading.value = true;
    error.value = null;
    
    try {
      const mergedParams = { ...params, ...overrideParams };
      let response;
      
      switch (method.toLowerCase()) {
        case 'get':
        case 'delete':
          response = await request[method](url, { params: mergedParams });
          break;
        case 'post':
        case 'put':
          response = await request[method](url, requestData);
          break;
        default:
          throw new Error(`Unsupported method: ${method}`);
      }
      
      data.value = response;
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  };
  
  // 立即执行请求
  if (immediate) {
    onMounted(fetchData);
  }
  
  // 监听参数变化,自动重新请求
  if (options.watchParams) {
    watchEffect(fetchData);
  }
  
  return {
    data,
    loading,
    error,
    fetchData
  };
}

(二)在组件中使用

代码语言:vue
复制
<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">{{ error.message }}</div>
    <div v-else>
      <ul>
        <li v-for="item in userList" :key="item.id">{{ item.name }}</li>
      </ul>
      <button @click="fetchUserList">Refresh</button>
    </div>
  </div>
</template>

<script>
import { useApi } from '@/composables/useApi';

export default {
  setup() {
    const { data: userList, loading, error, fetchData: fetchUserList } = useApi(
      '/api/users', 
      { 
        params: { page: 1, limit: 10 },
        watchParams: true
      }
    );
    
    return {
      userList,
      loading,
      error,
      fetchUserList
    };
  }
};
</script>

五、Vuex集成方案

(一)创建API模块

代码语言:javascript
代码运行次数:0
运行
复制
// src/store/modules/api.js
import { createSlice } from '@reduxjs/toolkit';
import { getUserList, createUser } from '@/api/user';

const apiSlice = createSlice({
  name: 'api',
  initialState: {
    userList: [],
    loading: false,
    error: null
  },
  reducers: {
    fetchUsersStart(state) {
      state.loading = true;
      state.error = null;
    },
    fetchUsersSuccess(state, action) {
      state.loading = false;
      state.userList = action.payload;
    },
    fetchUsersFailure(state, action) {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

export const { fetchUsersStart, fetchUsersSuccess, fetchUsersFailure } = apiSlice.actions;

// 异步action
export const fetchUsers = (params) => async (dispatch) => {
  try {
    dispatch(fetchUsersStart());
    const data = await getUserList(params);
    dispatch(fetchUsersSuccess(data));
  } catch (error) {
    dispatch(fetchUsersFailure(error.message));
  }
};

export default apiSlice.reducer;

(二)在组件中使用

代码语言:vue
复制
<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">{{ error }}</div>
    <div v-else>
      <ul>
        <li v-for="user in userList" :key="user.id">{{ user.name }}</li>
      </ul>
    </div>
  </div>
</template>

<script>
import { useSelector, useDispatch } from 'react-redux';
import { fetchUsers } from '@/store/modules/api';

export default {
  setup() {
    const dispatch = useDispatch();
    const { userList, loading, error } = useSelector(state => state.api);
    
    // 组件挂载时获取数据
    onMounted(() => {
      dispatch(fetchUsers({ page: 1, limit: 10 }));
    });
    
    return {
      userList,
      loading,
      error
    };
  }
};
</script>

六、TypeScript支持

(一)定义API接口类型

代码语言:typescript
复制
// src/api/types.ts
export interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

export interface UserListParams {
  page: number;
  limit: number;
  keyword?: string;
}

export interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

(二)类型化API请求

代码语言:typescript
复制
// src/api/user.ts
import request from '@/utils/request';
import { User, UserListParams, ApiResponse } from './types';

/**
 * 获取用户列表
 * @param {UserListParams} params 查询参数
 * @returns {Promise<User[]>}
 */
export function getUserList(params: UserListParams): Promise<User[]> {
  return request<ApiResponse<User[]>>({
    url: '/api/users',
    method: 'get',
    params
  }).then(res => res.data);
}

/**
 * 创建用户
 * @param {Partial<User>} data 用户数据
 * @returns {Promise<User>}
 */
export function createUser(data: Partial<User>): Promise<User> {
  return request<ApiResponse<User>>({
    url: '/api/users',
    method: 'post',
    data
  }).then(res => res.data);
}

通过以上封装方案,你可以在Vue项目中高效、统一地处理API请求。根据项目规模和需求的不同,可以选择合适的封装方式,从基础封装到高级封装逐步提升,最终实现一个灵活、可维护的API请求层。



代码实现:请阅读原文查看

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Vue中封装Axios的技术方案与实践
    • 一、Axios简介与Vue集成必要性
      • (一)Axios基本特性
      • (二)Vue集成Axios的优势
    • 二、基础封装方案
      • (一)安装与基本配置
      • (二)模块化API封装
    • 三、高级封装方案
      • (一)支持多种请求方式的封装
      • (二)错误处理增强
      • (三)请求取消功能
    • 四、Vue3组合式API封装示例
      • (一)使用组合式函数封装
      • (二)在组件中使用
    • 五、Vuex集成方案
      • (一)创建API模块
      • (二)在组件中使用
    • 六、TypeScript支持
      • (一)定义API接口类型
      • (二)类型化API请求
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档