Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主
Posted 小贼先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主相关的知识,希望对你有一定的参考价值。
前戏
在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了?
1 let axios = function(config){ 2 return new Promise((res, rej) => { 3 4 // 发送ajax请求,一般使用$.ajax() 5 ajax({ 6 ...config, 7 success(data){ 8 res(data); 9 }, 10 error(e){ 11 rej(e); 12 } 13 }) 14 }) 15 }
然后就可以 axios(...).then().catch()的使用了。
通过上面简单代码,我们就已经清楚了axios基本的实现原理:
内部通过Promise对XHR对象及其事件进行封装,
然后外部就可以通过操作Promise的方式进行异步处理了。
是不是很简单,那接下来就开始我们正式的讲解吧
为url、data、headers添加统一处理方法
1 /** 2 * 2,为url、data、headers添加统一处理方法 3 */ 4 let axios = function(config) { 5 return new Promise((res, rej) => { 6 7 // 为url、data、headers添加统一处理方法 8 // (此处只为展示大概架构,暂时不必关心方法的实现,下面会做详细介绍) 9 let { url, method, data, params, headers } = config; 10 url = buildURL(combineURLs(config.baseURL, url), params); 11 headers = merge(headers.common, headers); 12 data = transformData(data, headers); 13 14 // 发送ajax请求,一般使用$.ajax() 15 ajax({ 16 ...config, 17 url, 18 data, 19 headers, 20 success(data) { 21 res(data); 22 }, 23 error(e) { 24 rej(e); 25 } 26 }) 27 }) 28 }
放弃ajax方法,对原生XHR进行简单封装
/** * 3,放弃ajax方法,对原生XHR进行简单封装 */ let axios = function (config) { // 默认配置项 let defaultConfig = { method: ‘get‘, responseType: ‘json‘, timeout: 0, } return new Promise((res, rej) => { config = merge(defaultConfig, config); // 为url、data、headers添加统一处理方法 let { url, method, data, params, headers } = config; url = buildURL(combineURLs(config.baseURL, url), params); headers = merge(headers.common, headers); data = transformData(data, headers); // 创建xhr实例 var xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.responseType = config.responseType; xhr.timeout = config.timeout; request.addEventListener(‘progress‘, config.onDownloadProgress); xhr.onload = function () { // 先要判断 xhr.status逻辑 res(xhr.response) }; xhr.onerror = function () { rej(‘Network Error‘) }; xhr.ontimeout = function handleTimeout() { rej(‘timeout‘); }; xhr.send(data); }) }
改用class来实现一个简单的axios
1 /** 2 * 4,改用class来实现一个简单的axios 3 */ 4 class Axios { 5 constructor(instanceConfig){ 6 this.defaults = instanceConfig; 7 } 8 request(config){ 9 10 this.config = { 11 ...this.defaults, 12 ...config, 13 } 14 this.dispatchRequest(); 15 16 } 17 dispatchRequest(){ 18 // 为url、data、headers添加统一处理方法 19 let { url, method, data, params, headers } = this.config; 20 url = buildURL(combineURLs(config.baseURL, url), params); 21 headers = merge(headers.common, headers); 22 data = transformData(data, headers); 23 24 // 创建xhr实例 25 var xhr = new XMLHttpRequest(); 26 xhr.open(method, url); 27 xhr.responseType = config.responseType; 28 xhr.timeout = config.timeout; 29 request.addEventListener(‘progress‘, config.onDownloadProgress); 30 xhr.onload = function () { 31 // 先要判断 xhr.status逻辑 32 res(xhr.response) 33 }; 34 xhr.onerror = function () { 35 rej(‘Network Error‘) 36 }; 37 xhr.ontimeout = function handleTimeout() { 38 rej(‘timeout‘); 39 }; 40 xhr.send(data); 41 } 42 } 43 44 // 默认配置项 45 let defaultConfig = { 46 method: ‘get‘, 47 responseType: ‘json‘, 48 timeout: 0, 49 } 50 51 // 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式 52 function createInstance(defaultConfig){ 53 let context = new Axios(defaultConfig); 54 let instance = Axios.prototype.request.bind(context); 55 extend(instance, Axios.prototype, context); 56 extend(instance, context); 57 } 58 59 let axios = createInstance(defaultConfig);
添加get、post、put等简写用法
/** * 5, 添加get、post、put等简写用法 */ class Axios { constructor(instanceConfig) { this.defaults = instanceConfig; } request(config) { this.config = { ...this.defaults, ...config, } this.dispatchRequest(); } dispatchRequest() { // 为url、data、headers添加统一处理方法 let { url, method, data, params, headers } = this.config; url = buildURL(combineURLs(config.baseURL, url), params); headers = merge(headers.common, headers); data = transformData(data, headers); // 创建xhr实例 var xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.responseType = config.responseType; xhr.timeout = config.timeout; request.addEventListener(‘progress‘, config.onDownloadProgress); xhr.onload = function () { // 先要判断 xhr.status逻辑 res(xhr.response) }; xhr.onerror = function () { rej(‘Network Error‘) }; xhr.ontimeout = function handleTimeout() { rej(‘timeout‘); }; xhr.send(data); } } let noDataMethods = [‘delete‘, ‘get‘, ‘head‘, ‘options‘]; let hasDataMethods = [‘post‘, ‘put‘, ‘patch‘]; for (const method of noDataMethods) { Axios.prototype[method] = function (url, config = {}){ return this.request({ ...config, method: method, url: url, }); } } for (const method of hasDataMethods) { Axios.prototype[method] = function (url, data, config = {}) { return this.request({ ...config, method: method, url: url, data, }); } } // 默认配置项 let defaultConfig = { method: ‘get‘, responseType: ‘json‘, timeout: 0, } // 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式 function createInstance(defaultConfig) { let context = new Axios(defaultConfig); let instance = Axios.prototype.request.bind(context); extend(instance, Axios.prototype, context); extend(instance, context); } let axios = createInstance(defaultConfig);
增加拦截功能,在请求前修改配置和数据,在请求后修改返回结果
1 /** 2 * 6,增加拦截功能,在请求前修改配置和数据,在请求后修改返回结果 3 */ 4 class Axios { 5 constructor(instanceConfig) { 6 this.defaults = instanceConfig; 7 } 8 request(config) { 9 10 this.config = { 11 ...this.defaults, 12 ...config, 13 } 14 var chain = [dispatchRequest, undefined]; 15 var promise = Promise.resolve(config); 16 17 } 18 dispatchRequest(config) { 19 // 为url、data、headers添加统一处理方法 20 let { url, method, data, params, headers } = config; 21 url = buildURL(combineURLs(config.baseURL, url), params); 22 headers = merge(headers.common, headers); 23 data = transformData(data, headers); 24 25 // 创建xhr实例 26 var xhr = new XMLHttpRequest(); 27 xhr.open(method, url); 28 xhr.responseType = config.responseType; 29 xhr.timeout = config.timeout; 30 request.addEventListener(‘progress‘, config.onDownloadProgress); 31 xhr.onload = function () { 32 // 先要判断 xhr.status逻辑 33 res(xhr.response) 34 }; 35 xhr.onerror = function () { 36 rej(‘Network Error‘) 37 }; 38 xhr.ontimeout = function handleTimeout() { 39 rej(‘timeout‘); 40 }; 41 xhr.send(data); 42 } 43 } 44 let noDataMethods = [‘delete‘, ‘get‘, ‘head‘, ‘options‘]; 45 let hasDataMethods = [‘post‘, ‘put‘, ‘patch‘]; 46 for (const method of noDataMethods) { 47 Axios.prototype[method] = function (url, config = {}) { 48 return this.request({ 49 ...config, 50 method: method, 51 url: url, 52 }); 53 } 54 } 55 for (const method of hasDataMethods) { 56 Axios.prototype[method] = function (url, data, config = {}) { 57 return this.request({ 58 ...config, 59 method: method, 60 url: url, 61 data, 62 }); 63 } 64 } 65 66 // 默认配置项 67 let defaultConfig = { 68 method: ‘get‘, 69 responseType: ‘json‘, 70 timeout: 0, 71 } 72 73 // 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式 74 function createInstance(defaultConfig) { 75 let context = new Axios(defaultConfig); 76 let instance = Axios.prototype.request.bind(context); 77 extend(instance, Axios.prototype, context); 78 extend(instance, context); 79 } 80 81 let axios = createInstance(defaultConfig);
// 其他:
// 撤销请求abort
// 添加 xsrf header
未完待续...
以上是关于Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主的主要内容,如果未能解决你的问题,请参考以下文章