Axios是一个基于Promise的HTTP客户端,专为浏览器和Node.js设计,具有以下特性:
在Vue项目中使用Axios可以带来以下优势:
npm install axios
// 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;
// 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'
});
}
// 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;
// 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));
}
// 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);
}
);
// ...原有代码...
// 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);
}
);
}
}
// 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
};
}
<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>
// 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;
<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>
// 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;
}
// 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 删除。