在前端开发中,封装请求接口可以提高代码的可维护性、复用性和可读性。下面介绍几种常见的封装方式:
1、基于Fetch API的封装
// http.js
const baseURL = 'https://api.example.com'
async function request(url, options = {}) {
const headers = {
'Content-Type': 'application/json',
...options.headers
}
const response = await fetch(`${baseURL}${url}`, {
...options,
headers
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json()
}
export function get(url, options = {}) {
return request(url, {
...options,
method: 'GET'
})
}
export function post(url, data, options = {}) {
return request(url, {
...options,
method: 'POST',
body: JSON.stringify(data)
})
}
// 其他方法如 put, delete 等类似
2、基于Axios的封装
// http.js
import axios from 'axios'
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
instance.interceptors.request.use(
config => {
// 可以在这里添加token等
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
instance.interceptors.response.use(
response => {
// 对响应数据做点什么
return response.data
},
error => {
// 对响应错误做点什么
if (error.response) {
switch (error.response.status) {
case 401:
// 处理未授权
break
case 404:
// 处理未找到
break
// 其他状态码处理
}
}
return Promise.reject(error)
}
)
export default instance
3、更高级的封装(结合业务)
// api.js
import http from './http'
export const userAPI = {
login: (data) => http.post('/user/login', data),
getUserInfo: (id) => http.get(`/user/${id}`),
updateUser: (id, data) => http.put(`/user/${id}`, data),
deleteUser: (id) => http.delete(`/user/${id}`)
}
export const productAPI = {
getList: (params) => http.get('/products', { params }),
getDetail: (id) => http.get(`/products/${id}`),
create: (data) => http.post('/products', data)
// ...
}
4、使用 TypeScript 增强类型安全
// http.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
interface IResponse<T> {
code: number
message: string
data: T
}
class HttpClient {
private instance: AxiosInstance
constructor(baseURL: string) {
this.instance = axios.create({ baseURL })
this.setupInterceptors()
}
private setupInterceptors() {
// 请求拦截器
this.instance.interceptors.request.use(
(config) => {
// 添加token等
return config
},
(error) => Promise.reject(error)
)
// 响应拦截器
this.instance.interceptors.response.use(
(response: AxiosResponse<IResponse<any>>) => {
if (response.data.code !== 0) {
return Promise.reject(response.data.message)
}
return response.data.data
},
(error) => Promise.reject(error)
)
}
public async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.instance.get(url, config)
}
public async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.post(url, data, config)
}
// 其他方法...
}
export const http = new HttpClient('https://api.example.com')
使用示例
import { userAPI } from './api'
async function login() {
try {
const result = await userAPI.login({
username: 'admin',
password: '123456'
})
console.log('登录成功', result)
} catch (error) {
console.error('登录失败', error)
}
}
一次发出两个一模一样的请求如何清除一个,通过使用 Axios 的 CancelToken 和请求拦截器,如果已经有相同的请求在进行中,取消之前的请求
//axios封装
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// 封装 GET 请求
const get = (url, params, cancelToken) => {
return instance.get(url, { params, cancelToken });
};
// 封装 POST 请求
const post = (url, data, cancelToken) => {
return instance.post(url, data, { cancelToken });
};
export default {get,post,};
//使用 CancelToken 取消重复请求
import axios from 'axios';
import api from './api'; // 上面封装的 Axios 实例
const pendingRequests = new Map();
const CancelToken = axios.CancelToken;
const requestInterceptor = (config) => {
const { url, method, params, data } = config;
// 生成唯一标识符
const key = `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`;
if (pendingRequests.has(key)) {
// 如果已经有相同的请求在进行中,取消之前的请求
const cancelToken = pendingRequests.get(key);
cancelToken('Request canceled due to duplicate request');
}
// 创建新的取消令牌
const source = CancelToken.source();
config.cancelToken = source.token;
// 将新的请求加入到 pendingRequests 中
pendingRequests.set(key, source.cancel);
// 在请求完成后删除该请求
config.interceptors.response.use(
(response) => {
pendingRequests.delete(key);
return response;
},
(error) => {
pendingRequests.delete(key);
return Promise.reject(error);
}
);
return config;
};
// 添加请求拦截器
instance.interceptors.request.use(requestInterceptor);
// 示例请求
const fetchUserData = async (userId) => {
try {
const response = await api.get(`/users/${userId}`);
console.log(response.data);
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request was canceled', error.message);
} else {
console.error('Error:', error);
}
}
};
// 发起两个相同的请求
fetchUserData(1);
fetchUserData(1);
5.封装建议
-
统一错误处理:在拦截器中统一处理错误
-
请求取消:实现请求取消功能,避免重复请求
-
请求重试:对某些特定错误实现自动重试机制
-
缓存策略:根据业务需求实现缓存
-
加载状态:可以集成全局加载状态管理
-
Mock数据:开发环境下支持Mock数据
-
TypeScript支持:提供完整的类型定义
选择哪种封装方式取决于项目规模、团队习惯和技术栈。对于小型项目,简单的Fetch封装可能足够;对于大型项目,基于Axios的完整封装可能更合适。
6. 跨域问题解决(关键配套)
前端发送请求时可能遇到跨域限制(浏览器安全策略),需后端配合或前端配置:
后端:设置Access-Control-Allow-Origin等 CORS headers。
前端:通过Nginx 反向代理(部署时)或开发环境配置代理(如 Vue 的vite.config.js、React 的setupProxy.js)





网友评论