axiosソースシリーズ(四)---Axiosとdispatch Requestとブロック
15862 ワード
前言
これは、多くの一般的な要求ライブラリです.プラットフォームにまたがって実現され、プロミセに戻ってチェーンで呼び出されます.ソースを完全に通過したら、自分の要求ライブラリに対する理解を高めることができます.
axiosソースシリーズ(一)---カタログ構造とツール関数axiosソースシリーズ(二)---アダプター内部axiosソースシリーズ(三)---標準設定とキャンセル要求axiosソースシリーズ(四)---AxiosとdispatchRequestとブロック
axios作成
これがAxiosライブラリの露出した使い方です.
axios/lib/axios.js
上に疑問があるかもしれない点はこの2点にあります.余計なことをしたように見えます. を指定する. である.
axios/lib/core/mergConfig.js
これは統合設定を担当する方法です.
中にはデフォルトで3つの優先度の設定があります. valueFroom Config 2 Keys:config 2内の関連属性がundefinedでないならconfig 2の値を指定します.ここではconfig 1の構成を考慮しません. mergDeepPropertiesKeys:深層要求ヘッド構造、config 2優先度はconfig 1より高く、まず対象構造を処理してから他の非undefinedタイプを処理する default ToConfig 2 Keys:直接に値を割り当てることができる要求ヘッダのリスト、config 2優先度はconfig 1 より高いです.
上記の3つの構成をクリアしたら、私達は得ることができます. axiosKeys:config 1とconfig 2は、上の3つのデフォルトの構成を組み合わせたマッピング配列 を含んでいます. otherKeys:config 2に です.
最後に、others Keysに関する構成も最終結果にまとめて返します.
axios/lib/helpers/spread.js
Axiosコア構造関数
axios/lib/core/Axios.js
これはAxiosの構造関数です.は、最初のパラメータ着信要求アドレスの呼び出し方式をサポートし、デフォルト構成と着信構成 を統合する. になります.は、 を完了する.
迎撃器
axios/lib/core/InterceptorManager.js
スクリーンショットの構造関数を定義します.は、 を内蔵した例を生成する.プロトタイプバインディング に戻る.プロトタイプバインディング を削除する.プロトタイプバインディング を巡回する方法である.
スクリーンショットの例
axios/lib/core/dispatchRequest.js
これはスケジューリング要求を担当する関数で、Promiseに戻ります.は、要求がキャンセルされたらエラーが発生したことを検出した. は、要求ヘッドが に存在することを確認する.は、データ及び要求ヘッダに着信し、データタイプ変換要求データ は、要求ヘッダの にマージする. header属性の不要な属性を削除する .確認 は、 成功 は、要求がキャンセルされたらエラーが発生したことを検出した. そうでなければ、処理はデータフォーマット に戻る.
失敗しました そうでなければ、直接データ に戻ります.
axios/lib/core/transformData.js
解析要求/応答データの担当は、単に
これは、多くの一般的な要求ライブラリです.プラットフォームにまたがって実現され、プロミセに戻ってチェーンで呼び出されます.ソースを完全に通過したら、自分の要求ライブラリに対する理解を高めることができます.
axiosソースシリーズ(一)---カタログ構造とツール関数axiosソースシリーズ(二)---アダプター内部axiosソースシリーズ(三)---標準設定とキャンセル要求axiosソースシリーズ(四)---AxiosとdispatchRequestとブロック
axios作成
これがAxiosライブラリの露出した使い方です.
axios/lib/axios.js
var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);
axiosの例は、実際にAxiosコンストラクタによって作成され、プロトタイプのrequest
のthisをaxiosに向けた例であり、その後、utils.extend
の拡張関数を呼び出して、axios.prototype
とcontext
をaxiosの例に追加します.上に疑問があるかもしれない点はこの2点にあります.余計なことをしたように見えます.
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
しかし、ここのextend
には単に拡張を行うだけでなく、結合this指向の機能も追加されていますので、2回の操作で目的が違っています.instance
は拡張対象であり、Axiox.prototype
は拡張対象であり、context
は拡張対象function
であるthis
はオブジェクトinstance
は依然として拡張対象であるが、今回のcontext
は拡張対象// Expose Axios class to allow class inheritance
axios.Axios = Axios;
// Factory for creating new instances
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
内部がどのように配置されているか見てみます.axios/lib/core/mergConfig.js
これは統合設定を担当する方法です.
var utils = require('../utils');
/**
* Config-specific merge-function which creates a new config-object
* by merging two configuration objects together.
*
* @param {Object} config1
* @param {Object} config2
* @returns {Object} New object resulting from merging config2 to config1
*/
module.exports = function mergeConfig(config1, config2) {
// eslint-disable-next-line no-param-reassign
config2 = config2 || {};
var config = {};
var valueFromConfig2Keys = ['url', 'method', 'params', 'data'];
var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy'];
var defaultToConfig2Keys = [
'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer',
'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress',
'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent',
'httpsAgent', 'cancelToken', 'socketPath'
];
utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
}
});
utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) {
if (utils.isObject(config2[prop])) {
config[prop] = utils.deepMerge(config1[prop], config2[prop]);
} else if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
} else if (utils.isObject(config1[prop])) {
config[prop] = utils.deepMerge(config1[prop]);
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = config1[prop];
}
});
utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = config1[prop];
}
});
var axiosKeys = valueFromConfig2Keys
.concat(mergeDeepPropertiesKeys)
.concat(defaultToConfig2Keys);
var otherKeys = Object
.keys(config2)
.filter(function filterAxiosKeys(key) {
return axiosKeys.indexOf(key) === -1;
});
utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
} else if (typeof config1[prop] !== 'undefined') {
config[prop] = config1[prop];
}
});
return config;
};
呼び出しの方式から入手できます.config 1はaxiosのデフォルト設定で、config 2はユーザがカスタマイズした構成です.中にはデフォルトで3つの優先度の設定があります.
上記の3つの構成をクリアしたら、私達は得ることができます.
axiosKeys
に含まれていない構成属性をフィルタリングします.基本的には非常用またはカスタムフィールドです.config 1を考慮しない場合も最後に、others Keysに関する構成も最終結果にまとめて返します.
// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
axiosの例に方法を追加します.まずspread
がどのような効果があるかを見ます.axios/lib/helpers/spread.js
/**
* Syntactic sugar for invoking a function and expanding an array for arguments.
*
* Common use case would be to use `Function.prototype.apply`.
*
* ```js
* function f(x, y, z) {}
* var args = [1, 2, 3];
* f.apply(null, args);
* ```
*
* With `spread` this example can be re-written.
*
* ```js
* spread(function(x, y, z) {})([1, 2, 3]);
* ```
*
* @param {Function} callback
* @returns {Function}
*/
module.exports = function spread(callback) {
return function wrap(arr) {
return callback.apply(null, arr);
};
};
ただの文法飴です.黒魔法はありません.Axiosコア構造関数
axios/lib/core/Axios.js
これはAxiosの構造関数です.
var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');
/**
* Create a new instance of Axios
*
* @param {Object} instanceConfig The default config for the instance
*/
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
Axiosのインスタンスを作成すると、デフォルトでは設定が保存されます.内部ブロックはそれぞれ要求と応答に与えられます./**
* Dispatch a request
*
* @param {Object} config The config specific for this request (merged with this.defaults)
*/
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
// Set config.method
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// Hook up interceptors middleware
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
requestは二つの部分のロジックを分けます.chain
はフックの中間部品としてチェーンコールを行い、最終的に[
this.interceptors.request.fulfilled, this.interceptors.request.rejected,//
dispatchRequest, undefined,//
this.interceptors.response.fulfilled, this.interceptors.response.rejected,//
]
while
のチェーンを使用してchain
の関数を呼び出し、開始から受信までの完全なフローAxios.prototype.getUri = function getUri(config) {
//
config = mergeConfig(this.defaults, config);
// URL
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
module.exports = Axios;
すべての要求方式を巡回して、内蔵のrequest方法を呼び出します.迎撃器
axios/lib/core/InterceptorManager.js
スクリーンショットの構造関数を定義します.
var utils = require('./../utils');
function InterceptorManager() {
this.handlers = [];
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
関数全体は実はいくつかのことをしました.handlers
キューuse
によって登録された方法で、キューは、適合及び失敗関数方法を含むブロック対象に入り、該オブジェクトのキュー内のid(実際にはインデックス値)eject
は、指定されたidのブロッキングオブジェクトforEach
は、登録されたすべてのスクリーンセーバスクリーンショットの例
//
const reqInterceptor = axios.interceptors.request.use(function (config) {
//
return config;
}, function (error) {
//
return Promise.reject(error);
});
//
const resInterceptor = axios.interceptors.response.use(function (response) {
//
return response;
}, function (error) {
//
return Promise.reject(error);
});
//
axios.interceptors.request.eject(reqInterceptor);
axios.interceptors.request.eject(resInterceptor);
スケジュール要求axios/lib/core/dispatchRequest.js
これはスケジューリング要求を担当する関数で、Promiseに戻ります.
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
/**
* Throws a `Cancel` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
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) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
throwIfCancellationRequested
は、キャンセルされた要求があれば、エラーをスローしたということです.dispatchRequest
は、構成されたadapter
を使用して、サービス端末に要求を開始し、いくつかのステップに分けられる.common
と、現在の要求のmethods
とを第1層構造adapter
adapter(config)
を実行してPromiseに戻り、対応する動作を実行する.!isCancel(reason)
が成立すれば、要求がキャンセルされた場合にエラーが発生したことが検出される.axios/lib/core/transformData.js
解析要求/応答データの担当は、単に
utils.forEach
を使用しているだけです.var utils = require('./../utils');
/**
* Transform the data for a request or a response
*
* @param {Object|String} data The data to be transformed
* @param {Array} headers The headers for the request or response
* @param {Array|Function} fns A single function or Array of functions
* @returns {*} The resulting transformed data
*/
module.exports = function transformData(data, headers, fns) {
/*eslint no-param-reassign:0*/
utils.forEach(fns, function transform(fn) {
data = fn(data, headers);
});
return data;
};
これでaxiosライブラリ全体のソースコードはすでに説明済みです.