使用原生XMLHttpRequest实现Axios核心功能
概述
Axios是一个基于Promise的HTTP客户端,在浏览器和Node.js中均可使用。虽然Axios在浏览器端基于XMLHttpRequest实现,但它提供了更简洁的API和强大的功能,如拦截器、自动JSON转换和请求取消等。
本文将探讨如何使用原生XMLHttpRequest实现Axios的核心功能,帮助开发者深入理解网络请求的底层原理。
XMLHttpRequest基础
XMLHttpRequest (XHR) 是浏览器提供的API,用于在客户端和服务器之间传输数据。它支持各种HTTP方法(GET、POST、PUT、DELETE等)并能够处理多种数据格式。
基本使用示例:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
实现Axios核心功能
1. 创建请求函数
首先,我们需要创建一个类似axios的函数,它能够接受配置对象并返回Promise:
function request(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
// 设置请求方法和URL
xhr.open(config.method || 'GET', config.url, true);
// 设置请求头
if (config.headers) {
Object.keys(config.headers).forEach(key => {
xhr.setRequestHeader(key, config.headers[key]);
});
}
// 处理超时
if (config.timeout) {
xhr.timeout = config.timeout;
}
// 处理响应
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({
data: parseResponse(xhr),
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr),
config: config
});
} else {
reject(createError(xhr, config));
}
};
// 处理错误
xhr.onerror = function() {
reject(createError(xhr, config, 'Network Error'));
};
xhr.ontimeout = function() {
reject(createError(xhr, config, `Timeout of ${config.timeout}ms exceeded`));
};
// 发送请求数据
xhr.send(config.data || null);
});
}
2. 实现响应数据处理
Axios自动根据响应内容类型解析数据,我们需要实现类似功能:
function parseResponse(xhr) {
const contentType = xhr.getResponseHeader('Content-Type');
const response = xhr.responseText;
if (contentType && contentType.includes('application/json')) {
try {
return JSON.parse(response);
} catch (e) {
return response;
}
}
return response;
}
function parseHeaders(xhr) {
const headers = {};
const headerString = xhr.getAllResponseHeaders();
const lines = headerString.trim().split(/[\r\n]+/);
lines.forEach(line => {
const parts = line.split(': ');
const header = parts.shift();
const value = parts.join(': ');
headers[header] = value;
});
return headers;
}
3. 错误处理
Axios提供了详细的错误信息,我们需要模拟这一行为:
function createError(xhr, config, message) {
const error = new Error(message || `Request failed with status code ${xhr.status}`);
error.config = config;
error.code = xhr.status;
error.request = xhr;
error.response = {
data: parseResponse(xhr),
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr)
};
return error;
}
4. 实现快捷方法
像Axios一样,我们可以为常用HTTP方法提供快捷方式:
const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'];
httpMethods.forEach(method => {
exports[method] = function(url, config = {}) {
return request({
...config,
method: method.toUpperCase(),
url
});
};
});
5. 实现拦截器机制
拦截器是Axios的强大功能之一,我们可以模拟这一机制:
function createInterceptorManager() {
const handlers = [];
return {
use: function(fulfilled, rejected) {
handlers.push({
fulfilled,
rejected
});
return handlers.length - 1;
},
eject: function(id) {
if (handlers[id]) {
handlers[id] = null;
}
},
forEach: function(fn) {
handlers.forEach(h => {
if (h !== null) {
fn(h);
}
});
}
};
}
// 创建请求和响应拦截器
const interceptors = {
request: createInterceptorManager(),
response: createInterceptorManager()
};
6. 完整实现示例
结合以上各部分,我们可以创建一个简化的axios实现:
function createAxiosInstance() {
const instance = function(config) {
// 处理请求拦截器
let requestConfig = {...config};
const requestInterceptorChain = [];
interceptors.request.forEach(interceptor => {
requestInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
// 应用请求拦截器
while (requestInterceptorChain.length) {
const fulfilled = requestInterceptorChain.shift();
const rejected = requestInterceptorChain.shift();
try {
requestConfig = fulfilled(requestConfig);
} catch (error) {
rejected(error);
break;
}
}
// 发送请求
return request(requestConfig).then(response => {
// 处理响应拦截器
const responseInterceptorChain = [];
interceptors.response.forEach(interceptor => {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
let finalResponse = response;
// 应用响应拦截器
while (responseInterceptorChain.length) {
const fulfilled = responseInterceptorChain.shift();
const rejected = responseInterceptorChain.shift();
try {
finalResponse = fulfilled(finalResponse);
} catch (error) {
rejected(error);
break;
}
}
return finalResponse;
});
};
// 添加快捷方法
httpMethods.forEach(method => {
instance[method] = function(url, config) {
return this({
...config,
method: method.toUpperCase(),
url
});
};
});
// 添加拦截器
instance.interceptors = interceptors;
return instance;
}
const axios = createAxiosInstance();
总结
通过使用原生XMLHttpRequest实现Axios的核心功能,我们可以更深入地理解现代HTTP客户端库的工作原理。虽然我们的实现缺少Axios的一些高级功能(如取消请求、CSRF保护等),但它涵盖了大部分常用功能。
这种实现方式不仅有助于学习目的,还能在某些无法使用第三方库的环境中提供类似的API体验。理解底层原理有助于我们更好地使用和调试基于Axios的应用程序。
需要注意的是,在实际项目中,通常建议使用官方Axios库,因为它经过充分测试、有良好的社区支持,并且包含了许多我们未实现的边缘情况处理。
可以进扣扣裙(651706395),互相交流!

浙公网安备 33010602011771号