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

 

 

未完待续...

 

相关文章