這應該是一個大多數都經常使用的請求庫,由於它能夠支持多種配置,跨平臺實現,返回promise進行鏈式調用.徹底過一遍源碼能夠提高本身對請求庫的理解知識node
axios源碼系列(一) --- 目錄結構和工具函數
axios源碼系列(二) --- 適配器內部
axios源碼系列(三) --- 默認配置和取消請求
axios源碼系列(四) --- Axios和dispatchRequest與攔截器ios
這個Axios庫的默認參數配置json
var utils = require('./utils'); var normalizeHeaderName = require('./helpers/normalizeHeaderName'); var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' }; function setContentTypeIfUnset(headers, value) { if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; } } function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== 'undefined') { // For browsers use XHR adapter adapter = require('./adapters/xhr'); } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { // For node use HTTP adapter adapter = require('./adapters/http'); } return adapter; }
這裏只有四件事:axios
Content-Type
爲瀏覽器默認的編碼格式adapters
裏面解析頭部名函數的源碼以下segmentfault
過濾得出是否不一樣大小寫字母的等價請求頭,是的話則設置新的請求頭移除舊的請求頭promise
var utils = require('../utils'); module.exports = function normalizeHeaderName(headers, normalizedName) { utils.forEach(headers, function processHeader(value, name) { if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { headers[normalizedName] = value; delete headers[name]; } }); };
這是暴露出去的對象瀏覽器
var defaults = { adapter: getDefaultAdapter(), transformRequest: [function transformRequest(data, headers) { normalizeHeaderName(headers, 'Accept'); normalizeHeaderName(headers, 'Content-Type'); if (utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data) ) { return data; } if (utils.isArrayBufferView(data)) { return data.buffer; } if (utils.isURLSearchParams(data)) { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); return data.toString(); } if (utils.isObject(data)) { setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); return JSON.stringify(data); } return data; }], transformResponse: [function transformResponse(data) { /*eslint no-param-reassign:0*/ if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { /* Ignore */ } } return data; }], /** * A timeout in milliseconds to abort a request. If set to 0 (default) a * timeout is not created. */ timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, validateStatus: function validateStatus(status) { return status >= 200 && status < 300; } }; defaults.headers = { common: { 'Accept': 'application/json, text/plain, */*' } }; utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { defaults.headers[method] = {}; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); }); module.exports = defaults;
主要提供了幾個默認配置cookie
配置 | 做用 |
---|---|
adapter | 宿主環境對應的適配器 |
transformRequest | 內置轉換函數,根據對應格式設置請求 |
transformResponse | 內置轉換函數,若是是字符型則反序列化 |
timeout | 超時時間 |
xsrfCookieName | 用做 xsrf token 的值的cookie的名稱 |
xsrfHeaderName | 承載 xsrf token 的值的 HTTP 頭的名稱 |
maxContentLength | 定義容許的響應內容的最大尺寸 |
validateStatus | 內置對於給定的HTTP 響應狀態碼是 resolve 或 reject promise的函數 |
headers | 默認請求頭 |
['delete', 'get', 'head'] | 定義相關請求方式的默認請求頭{} |
[post', 'put', 'patch'] | 定義相關請求方式的默認請求頭,{'Content-Type': 'application/x-www-form-urlencoded'} |
/** * A `Cancel` is an object that is thrown when an operation is canceled. * * @class * @param {string=} message The message. */ function Cancel(message) { this.message = message; } Cancel.prototype.toString = function toString() { return 'Cancel' + (this.message ? ': ' + this.message : ''); }; Cancel.prototype.__CANCEL__ = true; module.exports = Cancel;
建立Cancel的構造函數,代表取消某個請求,提供一句取消信息和標記app
var Cancel = require('./Cancel'); /** * A `CancelToken` is an object that can be used to request cancellation of an operation. * * @class * @param {Function} executor The executor function. */ 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) { // Cancellation has already been requested return; } token.reason = new Cancel(message); resolvePromise(token.reason); }); } /** * Throws a `Cancel` if cancellation has been requested. */ CancelToken.prototype.throwIfRequested = function throwIfRequested() { if (this.reason) { throw this.reason; } }; /** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */ CancelToken.source = function source() { var cancel; var token = new CancelToken(function executor(c) { cancel = c; }); return { token: token, cancel: cancel }; }; module.exports = CancelToken;
建立CancelToken
對象用於取消請求的操做:函數
執行傳入的回調函數參數
reason
屬性表明已經發出取消請求的操做,則中斷操做Cancel
對象而且改變Promise狀態返回該對象原型上綁定throwIfRequested
方法,若是實例已經取消能夠直接拋出取消信息
構造函數上綁定source
方法,調用以後能夠返回新的CancelToken
實例和cancel
方法
module.exports = function isCancel(value) { return !!(value && value.__CANCEL__); };
經過是否擁有__CANCEL__
屬性而得知某次請求是否已經取消了.
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 處理錯誤 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消請求(message 參數是可選的) source.cancel('Operation canceled by the user.');
或者
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函數接收一個 cancel 函數做爲參數 cancel = c; }) }); // cancel the request cancel();