摘要:開始研究核心代碼這個類首先是構(gòu)造函數(shù)看完上面的內(nèi)容大家應(yīng)該有點印象,上掛了和,是默認(rèn)的配置,顧名思義就是攔截器,目測包含了和兩種類型。喜歡就點個贊吧參考文章源代碼重點難點分析源代碼重點難點分析
axios是一個基于promise的http庫,支持瀏覽器和node端,最近我在做beauty-we的api設(shè)計,研讀一個成熟的http庫勢在必行,axios功能完整、api簡潔、注釋清晰,再適合不過,現(xiàn)在就讓我們開始吧~
準(zhǔn)備工作clone項目,打開dist文件夾,里面有axios.js和axios.min.js,看一下axios.js的第一段:
(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === "object" && typeof module === "object") { module.exports = factory(); } else if (typeof define === "function" && define.amd) { define([], factory); } else if (typeof exports === "object") { exports.axios = factory(); } else { root.axios = factory(); } })(this, function() {...我是主代碼部分...});
最外層是一個立即執(zhí)行函數(shù)(IIFE),傳入?yún)?shù)this和構(gòu)造函數(shù)factory,區(qū)分了4種情況:
exports和module都存在時,cmd模式;
define和define.amd都存在時,amd模式;
只有exports存在時;
既不是cmd也不是cmd模式時,掛載在傳入的宿主對象上。
我比較了其他3個類庫的處理(underscore/jquery/vue):
underscore(function() { var root = typeof self == "object" && self.self === self && self ||typeof global == "object" && global.global === global && global ||this ||{}; if (typeof exports != "undefined" && !exports.nodeType) { if (typeof module != "undefined" && !module.nodeType && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // ....這里是主代碼部分... if (typeof define == "function" && define.amd) { define("underscore", [], function() { return _; }); } }());jquery
(function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } if ( typeof define === "function" && define.amd ) { define( "jquery", [], function() { return jQuery; } ); } })(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // ...這里是主代碼部分 })vue
(function (global, factory) { typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global.Vue = factory()); }(this, (function () { // ...這里是主代碼部分... }))
4個庫中,
underscore和axios考慮了4種環(huán)境情況,vue和jquery無視了exports存在但沒有module的情況(這種情況什么時候出現(xiàn)我不清楚,知道的人求指出);環(huán)境判斷vue用了3目運算符,看起來又干凈又漂亮(其實這好像并不重要,壓縮后大家都一樣~);對amd的支持underscore和jquery都放在了代碼的尾部,打補丁的痕跡好明顯;
jquery強調(diào)了document一定存在,否則會拋出錯誤,axios和vue只是傳入了this,underscore則認(rèn)真區(qū)分了browser的window,server端的global,其他端的this,自創(chuàng)了一個self統(tǒng)一之,這意味著后三者可以存活在更普遍的環(huán)境中(比如axios in web worker,比如weex),underscore的做法最嚴(yán)謹(jǐn);
除了underscore,其他3者都把主代碼放在了IIFE的參數(shù)里,而underscore是直接寫在IIFE里,我個人還是喜歡用那個叫factory的函數(shù)。
看整體啰嗦了那么多之后,正式開始看axios啦。根據(jù)package.json里的npm run dev的指向,我們打開lib文件夾里的axios.js,這就是全局入口了。
./lib/axios.js只做了兩件事:
創(chuàng)建一個axios對象
暴露一些拓展方法出去
先看axios對象的創(chuàng)建:
var axios = createInstance(defaults); function createInstance(defaultConfig) { // 一切的開始,new一個實例出來 var context = new Axios(defaultConfig); var instance = bind(Axios.prototype.request, context); utils.extend(instance, Axios.prototype, context); utils.extend(instance, context); return instance; }
創(chuàng)建實例axios用了4步,第2步開始就比較奇怪了:
var instance = bind(Axios.prototype.request, context);
看一下bind函數(shù):
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); }; }
它返回一個wrap()函數(shù),其中執(zhí)行了參數(shù)1的函數(shù),綁定了參數(shù)2的上下文,也就是說,instance是一個叫wrap的函數(shù),它里面執(zhí)行了Axios原型對象上的request方法,同時綁定了最開始創(chuàng)建的那個實例的上下文;
再看下一行:
utils.extend(instance, Axios.prototype, context);
看一下extend函數(shù):
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; }
它承擔(dān)了兩個任務(wù),當(dāng)參數(shù)3存在時,它循環(huán)參數(shù)2,把參數(shù)2 上的屬性是函數(shù)類型的都用bind的方式掛載到參數(shù)1身上;否則的話,它實現(xiàn)了簡單的把參數(shù)2的復(fù)制給參數(shù)1;
那么看最后一行就知道了,3-4實現(xiàn)的是把Axios原型上的函數(shù)屬性都掛載到instance,這是第2行的一個循環(huán)版本,最后把axios實例復(fù)制給instance,形成一個真正的instance,此時,這個instance具備Axios實例的靜態(tài)屬性(復(fù)制來的),Axios.prototype上所有函數(shù)方法(通過bind,返回一個wrap(),里面調(diào)用Axios.prototype上的方法),instance本身還是一個wrap版本的Axios.prototype.request方法。
這時我好想問一個:為什么這么做。。
思考好久找到突破口,就是最奇怪的最后一點,axios是一個wrap函數(shù),包含了request,這其實是因為axios支持的兩種調(diào)用方式:
axios({config}).then()和axios.get("url").then()
前者確定了axios自身作為一個函數(shù)的特性,同時,它還需要具有g(shù)et/post等一堆方法,于是我翻到的commit最初的版本是:
var defaultInstance = new Axios(); var axios = module.exports = bind(Axios.prototype.request, defaultInstance);
這里還少了把defaultInstance上的靜態(tài)屬性復(fù)制給axios的過程,當(dāng)時Axios上的靜態(tài)屬性還很少,只有defaults,被手動掛載給axios:axios.defaults=defaults;,
隨著Axios添加了InterceptorManager攔截器,進(jìn)化成了第二版本:
var defaultInstance = new Axios(); var axios = module.exports = bind(Axios.prototype.request, defaultInstance); axios.defaults=defaults; axios.interceptors = defaultInstance.interceptors;
但是有人發(fā)現(xiàn)了一個bug,因為axios自帶create方法,可以自定義一個實例,而create內(nèi)寫的卻是極度簡陋的:
axios.create = function (defaultConfig) { return new Axios(defaultConfig); };
大家發(fā)現(xiàn)axios.create出來的和axios比較后api少很多啊,于是終極版本的構(gòu)建函數(shù)createInstance誕生了:
function createInstance(defaultConfig) { // 一切的開始,new一個實例出來,然而考慮到它不是最終對象,只能稱之為上下文 var context = new Axios(defaultConfig); // 這是最后返回的實例,本身是一個request函數(shù) var instance = bind(Axios.prototype.request, context); // 為了拓展性,一口氣把原型對象上所有的方法都拷一份 utils.extend(instance, Axios.prototype, context); // 把實例的靜態(tài)屬性復(fù)制過來 utils.extend(instance, context); return instance; }
終于創(chuàng)建完axios對象,接下來是暴露一些api出去:
// Factory for creating new instances axios.create = function create(instanceConfig) { return createInstance(mergeConfig(axios.defaults, instanceConfig)); }; // Expose Axios class to allow class inheritance axios.Axios = Axios; axios.all = function all(promises) { return Promise.all(promises); }; axios.spread =function spread(callback) { return function wrap(arr) { return callback.apply(null, arr); }; } axios.Cancel... axios.CancelToken... axios.isCancel...
入口文件的分析就到此結(jié)束了。
core Axios開始研究核心代碼Axios這個類;
首先是構(gòu)造函數(shù):
function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; }
看完上面的內(nèi)容大家應(yīng)該有點印象,axios上掛了defaults和interceptors,defaults是默認(rèn)的config配置,interceptors顧名思義就是攔截器,目測包含了request和response兩種類型。
接下來看個簡單的部分:
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 })); }; });
axios把7種請求都轉(zhuǎn)向了request方法,根據(jù)是否要帶data寫了2個循環(huán),看起來還挺簡潔的。
接下來看最核心的request代碼,這里的處理很巧妙很優(yōu)雅:
Axios.prototype.request = function request(config) { // ...我是很多config處理...此時不關(guān)心 // 創(chuàng)建了一個事件數(shù)組,第一位的是發(fā)送請求的動作,這是默認(rèn)狀態(tài) var chain = [dispatchRequest, undefined]; // 創(chuàng)建了一個promise對象 var promise = Promise.resolve(config); // 循環(huán)request攔截器,有的話就添加到chain的頭部 this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); // 循環(huán)response攔截器,有的話就添加到chain的尾部 this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); // 利用while循環(huán),執(zhí)行完所有事件 while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } // 返回最終的promise對象 return promise; }
核心axios對象到此已看完,下一節(jié)計劃分析dispatchRequest---adapter,真正發(fā)送請求的部分。喜歡就點個贊吧
參考文章:
AXIOS源代碼重點難點分析
es6-promise源代碼重點難點分析
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/95851.html
摘要:總所周知,的策略讓每次都要發(fā)送碼驗證,為了方便,我在的里作了前置攔截。結(jié)果不幸從此發(fā)生最開始沒有看官方文檔,以為應(yīng)該加在里面,又沒有考慮到要上傳格式的文檔,所以直接結(jié)果發(fā)送的是。這很正常,閱讀源碼知為時會自動添加的頭。不加又以上傳了。 總所周知,egg的csrf策略讓post每次都要發(fā)送token碼驗證,為了方便,我在axios的interceptor里作了前置攔截。 結(jié)果不幸從此發(fā)生...
摘要:我們先來看看構(gòu)造函數(shù)構(gòu)造函數(shù)就是用來實現(xiàn)攔截器的,這個構(gòu)造函數(shù)原型上有個方法。關(guān)于源碼,其實是比較簡單的,都是用來操作該構(gòu)造函數(shù)的實例屬性的。存放攔截器方法,數(shù)組內(nèi)每一項都是有兩個屬性的對象,兩個屬性分別對應(yīng)成功和失敗后執(zhí)行的函數(shù)。 Axios源碼分析 - XHR篇 文章源碼托管在github上,歡迎fork指正! axios 是一個基于 Promise 的http請求庫,可以用在瀏覽...
摘要:愛上閱讀,是一款的讀小說等書籍的并且閱讀的應(yīng)用。找了好久發(fā)現(xiàn)發(fā)現(xiàn)只有追書神器的暴露出來了,起點之類的找不到。八最后最后打個小廣告源碼都已在上開源,目前在逐步完善功能中。歡迎感興趣的同學(xué)和。 loveBook loveBook愛上閱讀,是一款webapp的讀小說等書籍的并且閱讀的應(yīng)用。如果覺得可以,歡迎fork和star。 自己最近在追斗破蒼穹電視劇,下班時候在地鐵上總聽到有人說,斗破蒼...
摘要:微豆一個使用與重構(gòu)豆瓣的項目。在中的配置代理重新啟動,打開查看結(jié)果是否與直接請求豆瓣相同。更多請參考豆瓣電影文檔。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。 微豆 Vdo 一個使用 Vue.js 與 Material Design 重構(gòu) 豆瓣 的項目。 項目網(wǎng)站 http://vdo.ralfz.com/ GitHub https:...
閱讀 1181·2023-04-26 02:42
閱讀 1633·2021-11-12 10:36
閱讀 1780·2021-10-25 09:47
閱讀 1262·2021-08-18 10:22
閱讀 1801·2019-08-30 15:52
閱讀 1213·2019-08-30 10:54
閱讀 2635·2019-08-29 18:46
閱讀 3496·2019-08-26 18:27