xhr、ajax、axios和fetch区别
xhr: 现代浏览器,最开始与服务器交换数据,都是通过XMLHttpRequest对象。它可以使用JSON、XML、HTML和text文本等格式发送和接收数据。
ajax: 为了方便操作dom并避免一些浏览器兼容问题,产生了jquery, 它里面的AJAX请求也兼容了不同的浏览器,可以直接使用.get、.pist。它就是对XMLHttpRequest对象的一层封装
Axios是一个基于promise的HTTP库,可以用在浏览器和 node.js 中。它本质也是对原生XMLHttpRequest的封装,只不过它是Promise的实现版本,符合最新的ES规范。
Fetch API提供了一个 JavaScript 接口,用于访问和操作HTTP管道的部分,例如请求和响应。它还提供了一个全局fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源
axios的特性
基于Promise
支持浏览器和nodejs环境
可添加请求、响应拦截器和转换请求和响应数据
请求可以取消、中断
自动转换JSON数据
客户端支持方法XSRF
带着问题去看代码
axios拦截器执行顺序?
源码分析 1、源码目录结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ├── /lib/ └── /adapters/ ├── http.js ├── xhr.js └── /cancel/ └── /helpers/ └── /core/ ├──Axios .js ├── createError.js ├── dispatchRequest.js ├── InterceptorManager .js ├── mergeConfig.js ├── settle.js ├── transformData.js └── axios.js └── defaults.js └── utils.js
2、入口文件/lib/axios.js 1 2 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 function createInstance (defaultConfig ) { const context = new Axios (defaultConfig); const instance = bind (Axios .prototype .request , context); utils.extend (instance, Axios .prototype , context, {allOwnKeys : true }); utils.extend (instance, context, null , {allOwnKeys : true }); instance.create = function create (instanceConfig ) { return createInstance (mergeConfig (defaultConfig, instanceConfig)); }; return instance; } const axios = createInstance (defaults);axios.default = axios; export default axios
3. Axios.prototype.request 1 2 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 class Axios { constructor (instanceConfig ) { this .defaults = instanceConfig; this .interceptors = { request : new InterceptorManager (), response : new InterceptorManager () }; } request (configOrUrl, config ) { const requestInterceptorChain = []; let 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 ); }); const responseInterceptorChain = []; this .interceptors .response .forEach (function pushResponseInterceptors (interceptor ) { responseInterceptorChain.push (interceptor.fulfilled , interceptor.rejected ); }); let promise; let i = 0 ; let len; if (!synchronousRequestInterceptors) { const chain = [dispatchRequest.bind (this ), undefined ]; chain.unshift .apply (chain, requestInterceptorChain); chain.push .apply (chain, responseInterceptorChain); len = chain.length ; promise = Promise .resolve (config); while (i < len) { promise = promise.then (chain[i++], chain[i++]); } return promise; } len = requestInterceptorChain.length ; let newConfig = config; i = 0 ; while (i < len) { const onFulfilled = requestInterceptorChain[i++]; const onRejected = requestInterceptorChain[i++]; try { newConfig = onFulfilled (newConfig); } catch (error) { onRejected.call (this , error); break ; } } try { promise = dispatchRequest.call (this , newConfig); } catch (error) { return Promise .reject (error); } i = 0 ; len = responseInterceptorChain.length ; while (i < len) { promise = promise.then (responseInterceptorChain[i++], responseInterceptorChain[i++]); } return promise; } } export default Axios ;
4. 拦截器实现 1 2 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 class InterceptorManager { constructor ( ) { this .handlers = []; } use (fulfilled, rejected, options ) { this .handlers .push ({ fulfilled, rejected, synchronous : options ? options.synchronous : false , runWhen : options ? options.runWhen : null }); return this .handlers .length - 1 ; } eject (id ) { if (this .handlers [id]) { this .handlers [id] = null ; } } clear ( ) { if (this .handlers ) { this .handlers = []; } } forEach (fn ) { utils.forEach (this .handlers , function forEachHandler (h ) { if (h !== null ) { fn (h); } }); } } export default InterceptorManager ;
由于请求的发送需要在请求拦截器之后,在响应拦截器之前,所以数组先放入request,接着在数组的前后分别加入请求和响应拦截器,由于加入请求拦截器的方法是unshift,所以最后通过promise进行请求的链式调用的时候,我们可以看到执行顺序是从左往右的,所以最后注册的请求拦截器会最先执行,而响应拦截的执行顺序和注册顺序是一样的。
5、dispatchRequest 我们进入到核心请求方法dispatchRequest中
1 2 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 function throwIfCancellationRequested (config ) { if (config.cancelToken ) { config.cancelToken .throwIfRequested (); } if (config.signal && config.signal .aborted ) { throw new CanceledError (null , config); } } export default function dispatchRequest (config ) { throwIfCancellationRequested (config); config.headers = AxiosHeaders .from (config.headers ); config.data = transformData.call ( config, config.transformRequest ); if (['post' , 'put' , 'patch' ].indexOf (config.method ) !== -1 ) { config.headers .setContentType ('application/x-www-form-urlencoded' , false ); } const adapter = adapters.getAdapter (config.adapter || defaults.adapter ); return adapter (config).then (function onAdapterResolution (response ) { throwIfCancellationRequested (config); response.data = transformData.call ( config, config.transformResponse , response ); response.headers = AxiosHeaders .from (response.headers ); return response; }, function onAdapterRejection (reason ) { if (!isCancel (reason)) { throwIfCancellationRequested (config); if (reason && reason.response ) { reason.response .data = transformData.call ( config, config.transformResponse , reason.response ); reason.response .headers = AxiosHeaders .from (reason.response .headers ); } } return Promise .reject (reason); }); }
6、适配器adapter 1 2 3 4 5 6 7 8 9 10 11 12 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; }
7、axios主动取消请求 终止一个尚未完成的网络请求, status状态为canceled
1、使用axios
1 2 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 import { CancelToken } from axios;const source = CancelToken .source ();axios .get ('/user' , { cancelToken : source.token , }) .catch (function (thrown ) { if (axios.isCancel (thrown)) { console .log ('主动取消' , thrown.message ); } else { console .error (thrown); } }); source.cancel ('我主动取消请求' )
2、CancelToken构造函数
1 2 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 class CancelToken { constructor (executor ) { if (typeof executor !== 'function' ) { throw new TypeError ('executor must be a function.' ); } let resolvePromise; this .promise = new Promise (function promiseExecutor (resolve ) { resolvePromise = resolve; }); const token = this ; this .promise .then (cancel => { if (!token._listeners ) return ; let i = token._listeners .length ; while (i-- > 0 ) { token._listeners [i](cancel); } token._listeners = null ; }); this .promise .then = onfulfilled => { let _resolve; const promise = new Promise (resolve => { token.subscribe (resolve); _resolve = resolve; }).then (onfulfilled); promise.cancel = function reject ( ) { token.unsubscribe (_resolve); }; return promise; }; executor (function cancel (message, config, request ) { if (token.reason ) { return ; } token.reason = new CanceledError (message, config, request); resolvePromise (token.reason ); }); } throwIfRequested ( ) { if (this .reason ) { throw this .reason ; } } subscribe (listener ) { if (this .reason ) { listener (this .reason ); return ; } if (this ._listeners ) { this ._listeners .push (listener); } else { this ._listeners = [listener]; } } unsubscribe (listener ) { if (!this ._listeners ) { return ; } const index = this ._listeners .indexOf (listener); if (index !== -1 ) { this ._listeners .splice (index, 1 ); } } static source ( ) { let cancel; const token = new CancelToken (function executor (c ) { cancel = c; }); return { token, cancel }; } } export default CancelToken ;
3、请求中是如何处理的cancel
1 2 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 if (config.cancelToken || config.signal ) { onCanceled = cancel => { if (!request) { return ; } reject (!cancel || cancel.type ? new CanceledError (null , config, request) : cancel); request.abort (); request = null ; }; config.cancelToken && config.cancelToken .subscribe (onCanceled); if (config.signal ) { config.signal .aborted ? onCanceled () : config.signal .addEventListener ('abort' , onCanceled); } } let onCanceled;function done ( ) { if (config.cancelToken ) { config.cancelToken .unsubscribe (onCanceled); } if (config.signal ) { config.signal .removeEventListener ('abort' , onCanceled); } }
文章来源于
class static静态方法