工具类
bind
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | function bind(fn, thisArg) {return function wrap() {
 var args = new Array(arguments.length);
 for (var i = 0; i < args.length; i++) {
 args[i] = arguments[i];
 }
 return fn.apply(thisArg, args);
 };
 };
 
 | 
extend
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | function extend(a, b, thisArg) {forEach(b, function assignValue(val, key) {
 if (thisArg && typeof val === 'function') {
 a[key] = bind(val, thisArg);
 } else {
 a[key] = val;
 }
 });
 return a;
 }
 
 | 
merge
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | function merge() {var result = {};
 function assignValue(val, key) {
 if (isPlainObject(result[key]) && isPlainObject(val)) {
 result[key] = merge(result[key], val);
 } else if (isPlainObject(val)) {
 result[key] = merge({}, val);
 } else if (isArray(val)) {
 result[key] = val.slice();
 } else {
 result[key] = val;
 }
 }
 
 for (var i = 0, l = arguments.length; i < l; i++) {
 forEach(arguments[i], assignValue);
 }
 return result;
 }
 
 | 
从使用方式看起
对几种调用方式做了编号
- axios(option)
- axios(url[, option])
- axios[method](url[, option])(- get、delete等方法)
- axios[method](url[, data[, option]])(- post、put等方法)
- axios.request(option)
让我们看看源码他是怎么做到能够支持这么多调用方式的
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | 
 function createInstance(defaultConfig) {
 var context = new Axios(defaultConfig);
 
 
 
 var instance = bind(Axios.prototype.request, context);
 
 
 
 
 utils.extend(instance, Axios.prototype, context);
 
 
 
 utils.extend(instance, context);
 
 return instance;
 }
 
 
 var axios = createInstance(defaults);
 
 | 
主要是为了能够做到1、2种调用方式,所以是返回request函数,并在其上挂载Axios实例的方法
接下来看看上述代码操作的主体:Axios、Axios.prototype.request 以及 Axios.prototype上的请求方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | function Axios(instanceConfig) {
 this.defaults = instanceConfig;
 this.interceptors = {
 request: new InterceptorManager(),
 response: new InterceptorManager()
 };
 }
 Axios.prototype.request = function request(config) {
 if (typeof config === 'string') {
 config = arguments[1] || {};
 config.url = arguments[0];
 } else {
 config = config || {};
 }
 
 };
 
 
 utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
 Axios.prototype[method] = function(url, config) {
 return this.request(mergeConfig(config || {}, {
 method: method,
 url: url,
 data: (config || {}).data
 }));
 };
 });
 
 utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
 Axios.prototype[method] = function(url, data, config) {
 return this.request(mergeConfig(config || {}, {
 method: method,
 url: url,
 data: data
 }));
 };
 });
 
 | 
结合上面createInstance的代码,我们可以看到为什么我们可以使用多种方式调用axios
我们先不看具体请求逻辑是怎么实现的,先看看拦截器是怎么做到在请求前后预处理的。
拦截器(interceptor)
在上文Axios的构造器中我们可以看到
| 12
 3
 4
 
 | this.interceptors = {request: new InterceptorManager(),
 response: new InterceptorManager()
 };
 
 | 
InterceptorManager类用以管理拦截器
InterceptorManager类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | function InterceptorManager() {this.handlers = [];
 }
 
 
 InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
 this.handlers.push({
 fulfilled: fulfilled,
 rejected: rejected,
 synchronous: options ? options.synchronous : false,
 runWhen: options ? options.runWhen : null
 });
 return this.handlers.length - 1;
 };
 
 
 InterceptorManager.prototype.eject = function eject(id) {
 if (this.handlers[id]) {
 this.handlers[id] = null;
 }
 };
 
 
 InterceptorManager.prototype.forEach = function forEach(fn) {
 utils.forEach(this.handlers, function forEachHandler(h) {
 if (h !== null) {
 fn(h);
 }
 });
 };
 
 | 
request中InterceptorManager使用方式
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 
 | Axios.prototype.request = function request(config) {
 
 
 var requestInterceptorChain = [];
 this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
 
 if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
 return;
 }
 
 
 requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
 });
 
 var responseInterceptorChain = [];
 this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
 
 responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
 });
 
 var promise;
 
 
 var chain = [dispatchRequest, undefined];
 
 
 Array.prototype.unshift.apply(chain, requestInterceptorChain);
 
 chain.concat(responseInterceptorChain);
 
 
 promise = Promise.resolve(config);
 while (chain.length) {
 
 
 
 promise = promise.then(chain.shift(), chain.shift());
 }
 
 return promise;
 }
 
 | 
大致流程就是如此
synchronous
但是axios还为请求拦截器提供了synchronous选项,需要全部请求拦截器设置synchronous为true,那么就以同步的方式运行
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 
 | Axios.prototype.request = function request(config) {
 
 var requestInterceptorChain = [];
 
 var synchronousRequestInterceptors = true;
 this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
 if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
 return;
 }
 
 synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
 
 requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
 });
 
 var responseInterceptorChain = [];
 this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
 responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
 });
 
 var promise;
 
 if (!synchronousRequestInterceptors) {
 var chain = [dispatchRequest, undefined];
 
 Array.prototype.unshift.apply(chain, requestInterceptorChain);
 chain.concat(responseInterceptorChain);
 
 promise = Promise.resolve(config);
 while (chain.length) {
 promise = promise.then(chain.shift(), chain.shift());
 }
 
 return promise;
 }
 
 
 var newConfig = config;
 while (requestInterceptorChain.length) {
 var onFulfilled = requestInterceptorChain.shift();
 var onRejected = requestInterceptorChain.shift();
 try {
 newConfig = onFulfilled(newConfig);
 } catch (error) {
 onRejected(error);
 break;
 }
 }
 
 try {
 promise = dispatchRequest(newConfig);
 } catch (error) {
 return Promise.reject(error);
 }
 
 while (responseInterceptorChain.length) {
 promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
 }
 
 return promise;
 };
 
 | 
启发:链式调用
| 12
 3
 4
 
 | new Man("Hank").sleep(1).eat("supper").sleep(1).eat("me").sleepFirst(2);
 
 
 
 | 
写法放在文末
发起请求dispatchRequest
在拿到请求拦截器处理后的config后
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 
 | function dispatchRequest(config) {
 
 throwIfCancellationRequested(config);
 
 
 config.headers = config.headers || {};
 
 
 config.data = transformData.call(
 config,
 config.data,
 config.headers,
 config.transformRequest
 );
 
 
 config.headers = utils.merge(
 config.headers.common || {},
 config.headers[config.method] || {},
 config.headers
 );
 
 
 utils.forEach(
 ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
 function cleanHeaderConfig(method) {
 delete config.headers[method];
 }
 );
 
 
 var adapter = config.adapter || defaults.adapter;
 
 return adapter(config).then(function onAdapterResolution(response) {
 
 return response;
 }, function onAdapterRejection(reason) {
 
 return Promise.reject(reason);
 });
 };
 
 function throwIfCancellationRequested(config) {
 if (config.cancelToken) {
 config.cancelToken.throwIfRequested();
 }
 }
 
 | 
跨端实现(adapter)
适配器模式
- 是否有XMLHttpRequest对象来判断是否是web环境
- 是否有process对象来判断node环境
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | function getDefaultAdapter() {
 var adapter;
 if (typeof XMLHttpRequest !== 'undefined') {
 adapter = require('./adapters/xhr');
 } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
 adapter = require('./adapters/http');
 }
 return adapter;
 }
 
 | 
封装xhr
返回一个promise
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | function xhrAdapter(config) {
 return new Promise(function dispatchXhrRequest(resolve, reject) {
 
 var request = new XMLHttpRequest();
 var fullPath = buildFullPath(config.baseURL, config.url);
 
 request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
 request.timeout = config.timeout;
 
 request.onloadend = function onloadend() {
 
 settle(resolve, reject, response);
 request = null;
 };
 request.onabort = function handleError() {
 reject();
 request = null;
 };
 request.onerror = function handleError() {
 reject();
 request = null;
 };
 request.ontimeout = function handleTimeout() {
 reject();
 request = null;
 };
 request.send(requestData);
 });
 };
 
 | 
验证服务端的返回结果是否通过验证:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | function settle(resolve, reject, response) {
 var validateStatus = response.config.validateStatus;
 if (!response.status || !validateStatus || validateStatus(response.status)) {
 resolve(response);
 } else {
 reject();
 }
 };
 
 | 
同理将http封装成promise
取消请求(cancelToken)
如何使用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | import axios from 'axios'
 
 axios.get(url, {
 cancelToken: new axios.CancelToken(cancel => {
 if () {
 cancel('取消日志');
 }
 })
 });
 
 
 const CancelToken = axios.CancelToken;
 const source = CancelToken.source();
 axios.get(url, {
 cancelToken: source.token
 });
 source.cancel('取消日志');
 
 
 | 
源码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
 | function CancelToken(executor) {if (typeof executor !== 'function') {
 throw new TypeError('executor must be a function.');
 }
 
 var resolvePromise;
 this.promise = new Promise(function promiseExecutor(resolve) {
 resolvePromise = resolve;
 });
 
 var token = this;
 executor(function cancel(message) {
 if (token.reason) {
 return;
 }
 
 token.reason = new Cancel(message);
 resolvePromise(token.reason);
 });
 }
 
 
 CancelToken.source = function source() {
 var cancel;
 var token = new CancelToken(function executor(c) {
 cancel = c;
 });
 return {
 token: token,
 cancel: cancel
 };
 };
 
 
 if (config.cancelToken) {
 config.cancelToken.promise.then(function onCanceled(cancel) {
 if (!request) {
 return;
 }
 request.abort();
 reject(cancel);
 request = null;
 });
 }
 
 | 
启发
链式调用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 | class Man {constructor (name) {
 this.name = name;
 this.arr = [];
 console.log(`This is ${name}`);
 
 const executor = i => {
 if (i < this.arr.length) {
 this.arr[i]().then(value => {
 console.log(value);
 executor(i + 1);
 });
 }
 }
 
 Promise.resolve().then(()=> {
 executor(0)
 }, 0);
 }
 
 sleep(sec) {
 this.arr.push(() => {
 return new Promise(resolve => {
 setTimeout(() => {
 resolve(`Wale up after ${sec}`);
 }, sec * 1000);
 })
 })
 return this;
 }
 
 eat(food) {
 this.arr.push(() => {
 return Promise.resolve(`Eat ${food}`);
 })
 return this;
 }
 
 sleepFirst(sec) {
 this.arr.unshift(() => {
 return new Promise(resolve => {
 setTimeout(() => {
 resolve(`Wale up after ${sec}`);
 }, sec * 1000);
 })
 })
 
 return this;
 }
 }
 
 | 
参考
Axios源码深度剖析