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
未完待续…