axios源码中的10多个工具函数,值得一学~
大家好,我是若川。最近組織了源碼共讀活動(dòng),感興趣的可以點(diǎn)此加我微信 ruochuan12?參與,每周大家一起學(xué)習(xí)200行左右的源碼,共同進(jìn)步。同時(shí)極力推薦訂閱我寫(xiě)的《學(xué)習(xí)源碼整體架構(gòu)系列》?包含20余篇源碼文章。
本文來(lái)自讀者Ethan01投稿,寫(xiě)了axios源碼中的工具函數(shù)~非常值得一學(xué)。原文鏈接:https://juejin.cn/post/7042610679815241758
1.前言
歌德說(shuō)過(guò):讀一本好書(shū),就是在和高尚的人談話(huà)。
同理,讀優(yōu)秀的開(kāi)源項(xiàng)目的源碼,就是在和牛逼的大佬交流。
之前總覺(jué)得閱讀源碼是一件了不起的事情,是只有大佬才會(huì)去做的事。其實(shí)源碼也不是想象的那么難,至少有很多看得懂。 比如源碼中的工具函數(shù),就算是初級(jí)的前端開(kāi)發(fā)也是能夠看懂的。重要的是,要邁出這一步,閱讀源碼沒(méi)什么的。
閱讀本文,你將學(xué)到:
1、javascript、nodejs調(diào)試技巧及調(diào)試工具; 2、如何學(xué)習(xí)調(diào)試axios源碼; 3、如何學(xué)習(xí)優(yōu)秀開(kāi)源項(xiàng)目的代碼,應(yīng)用到自己的項(xiàng)目; 4、axios源碼中實(shí)用的工具函數(shù);2.環(huán)境準(zhǔn)備
2.1 讀開(kāi)源項(xiàng)目的貢獻(xiàn)指南
打開(kāi) axios[1] , 你會(huì)驚奇的發(fā)現(xiàn),這不是在瀏覽器中打開(kāi)了一個(gè)vscode嗎?你沒(méi)有看錯(cuò),確實(shí)是在瀏覽器中打開(kāi)了vscode,而且還打開(kāi)了axios的源碼。如果你仔細(xì)看了瀏覽器地址欄里的url, 你會(huì)發(fā)現(xiàn)github后多了1s,顧名思義,就是1s打開(kāi)github上的項(xiàng)目。一個(gè)小擴(kuò)展:在每一個(gè)github項(xiàng)目中的url里直接加上1s,就能在網(wǎng)頁(yè)版vscode中查看源碼了(不過(guò)貌似現(xiàn)在只能查看,不能調(diào)試,調(diào)試的話(huà)還是要把源碼clone到本地)。
開(kāi)源項(xiàng)目一般能在根目錄下的README.md文件或CONTRIBUTING.md中找到貢獻(xiàn)指南。貢獻(xiàn)指南中說(shuō)明了參與貢獻(xiàn)代碼的一些注意事項(xiàng),比如:代碼風(fēng)格、代碼提交注釋格式、開(kāi)發(fā)、調(diào)試等。
打開(kāi)CONTRIBUTING.md[2],可以看到在54行的內(nèi)容:
Running?sandbox?in?browser```bash $?npm?start #?Open?127.0.0.1:3000這里就是告訴我們?cè)谌绾卧跒g覽器中運(yùn)行項(xiàng)目的。
2.2 克隆項(xiàng)目并運(yùn)行
這里使用axios的版本是v0.24.0;
git?clone?https://github.com/axios/axios.gitcd?axiosnpm?start打開(kāi)?http://localhost:3000/這時(shí)候可以看到這么一個(gè)頁(yè)面:
image.png打開(kāi)瀏覽器的控制臺(tái),選中source選項(xiàng),然后在axios目錄中可以找到源碼,如下圖:
image.png這個(gè)axios.js就是入口文件,這時(shí)候就可以隨意打斷點(diǎn)進(jìn)行調(diào)試了。
其實(shí),閱讀所有源碼的流程都類(lèi)似,之所以說(shuō)的這么詳細(xì),是為了能夠讓沒(méi)有閱讀過(guò)源碼的同學(xué)也能夠跟著一步一步的閱讀起來(lái)。當(dāng)你讀完之后,肯定會(huì)有不少的收獲,把這個(gè)過(guò)程和收獲記錄下來(lái),慢慢的提升自己,早晚會(huì)成為大佬。
3. 工具函數(shù)
今天的主角是`utils.js`[3]文件, 以下列出了文件中的工具函數(shù):
3.1 isArray 判斷數(shù)組
var?toString?=?Object.prototype.toString;//?可以通過(guò)?`toString()`?來(lái)獲取每個(gè)對(duì)象的類(lèi)型 //?一般返回值是?Boolean?類(lèi)型的函數(shù),命名都以?is?開(kāi)頭 function?isArray(val)?{return?toString.call(val)?===?'[object?Array]'; }3.2 isUndefined 判斷Undefined
//?直接用`typeof`判斷 //?注意?typeof?null?===?'object' function?isUndefined(val)?{return?typeof?val?===?'undefined'; }3.3 isBuffer 判斷 buffer
//?先判斷不是?`undefined`和`null` //?再判斷?`val`存在構(gòu)造函數(shù),因?yàn)?#96;Buffer`本身是一個(gè)類(lèi) //?最后通過(guò)自身的`isBuffer`方法判斷function?isBuffer(val)?{return?val?!==?null?&&?!isUndefined(val)?&&?val.constructor?!==?null?&&?!isUndefined(val.constructor)&&?typeof?val.constructor.isBuffer?===?'function'?&&?val.constructor.isBuffer(val); }什么是Buffer?
JavaScript 語(yǔ)言自身只有字符串?dāng)?shù)據(jù)類(lèi)型,沒(méi)有二進(jìn)制數(shù)據(jù)類(lèi)型。
但在處理像TCP流或文件流時(shí),必須使用到二進(jìn)制數(shù)據(jù)。因此在 Node.js中,定義了一個(gè)Buffer 類(lèi),該類(lèi)用來(lái)創(chuàng)建一個(gè)專(zhuān)門(mén)存放二進(jìn)制數(shù)據(jù)的緩存區(qū)。詳細(xì)可以看 官方文檔[4] 或 更通俗易懂的解釋[5]。
因?yàn)閍xios可以運(yùn)行在瀏覽器和node環(huán)境中,所以?xún)?nèi)部會(huì)用到nodejs相關(guān)的知識(shí)。
3.4 isFormData 判斷FormData
//?`instanceof`?運(yùn)算符用于檢測(cè)構(gòu)造函數(shù)的?`prototype`?屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈上function?isFormData(val)?{return?(typeof?FormData?!==?'undefined')?&&?(val?instanceof?FormData); }//?instanceof?用法function?C()?{} function?D()?{}const?c?=?new?C()c?instanceof?C?//?output:?true???因?yàn)?Object.getPrototypeOf(c)?===?C.prototypec?instanceof?Object?//?output:?true???因?yàn)?Object.prototype.isPrototypeOf(c)c?instanceof?D?//?output:?false???因?yàn)?D.prototype?不在?c?的原型鏈上3.5 isObject 判斷對(duì)象
//?排除?`null`的情況 function?isObject(val)?{return?val?!==?null?&&?typeof?val?===?'object'; }3.6 isPlainObject 判斷 純對(duì)象
純對(duì)象:用{}或new Object()創(chuàng)建的對(duì)象。
function?isPlainObject(val)?{if?(Object.prototype.toString.call(val)?!==?'[object?Object]')?{return?false;}var?prototype?=?Object.getPrototypeOf(val);return?prototype?===?null?||?prototype?===?Object.prototype; }//?例子1 const?o?=?{name:?'jay} isPlainObject(o)?//?true//?例子2 const?o?=?new?Object() o.name?=?'jay' isPlainObject(o)???//?true//?例子3 function?C()?{} const?c?=?new?C() isPlainObject(c);??//?false//?其實(shí)就是判斷目標(biāo)對(duì)象的原型是不是`null`?或?`Object.prototype`3.7 isDate 判斷Date
function?isDate(val)?{return?Object.prototype.toString.call(val)?===?'[object?Date]'; }3.8 isFile 判斷文件類(lèi)型
function?isFile(val)?{return?Object.prototype.toString.call(val)?===?'[object?File]'; }3.9 isBlob 判斷Blob
function?isBlob(val)?{return?Object.prototype.toString.call(val)?===?'[object?Blob]'; }Blob 對(duì)象表示一個(gè)不可變、原始數(shù)據(jù)的類(lèi)文件對(duì)象。它的數(shù)據(jù)可以按文本或二進(jìn)制的格式進(jìn)行讀取。
3.10 isFunction 判斷函數(shù)
function?isFunction(val)?{return?Object.prototype.toString.call(val)?===?'[object?Function]'; }3.11 isStream 判斷是否是流
//?這里`isObject`、`isFunction`為上文提到的方法 function?isStream(val)?{return?isObject(val)?&&?isFunction(val.pipe); }3.12 isURLSearchParams 判斷URLSearchParams
function?isURLSearchParams(val)?{return?typeof?URLSearchParams?!==?'undefined'?&&?val?instanceof?URLSearchParams; }//?例子 const?paramsString?=?"q=URLUtils.searchParams&topic=api" const?searchParams?=?new?URLSearchParams(paramsString); isURLSearchParams(searchParams)?//?trueURLSearchParams 接口定義了一些實(shí)用的方法來(lái)處理 URL 的查詢(xún)字符串,詳情可看 MDN[6]:
var?paramsString?=?"q=URLUtils.searchParams&topic=api" var?searchParams?=?new?URLSearchParams(paramsString);for?(let?p?of?searchParams)?{console.log(p); }//?輸出? [?'q',?'URLUtils.searchParams'?] [?'topic',?'api'?]searchParams.has("topic")?===?true;?//?true searchParams.get("topic")?===?"api";?//?true searchParams.getAll("topic");?//?["api"] searchParams.get("foo")?===?null;?//?true searchParams.append("topic",?"webdev"); searchParams.toString();?//?"q=URLUtils.searchParams&topic=api&topic=webdev" searchParams.set("topic",?"More?webdev"); searchParams.toString();?//?"q=URLUtils.searchParams&topic=More+webdev" searchParams.delete("topic"); searchParams.toString();?//?"q=URLUtils.searchParams"3.13 trim 去除首尾空格
//?`trim`方法不存在的話(huà),用正則 function?trim(str)?{return?str.trim???str.trim()?:?str.replace(/^\s+|\s+$/g,?''); }3.14 isStandardBrowserEnv 判斷標(biāo)準(zhǔn)瀏覽器環(huán)境
function?isStandardBrowserEnv()?{if?(typeof?navigator?!==?'undefined'?&&?(navigator.product?===?'ReactNative'?||navigator.product?===?'NativeScript'?||navigator.product?===?'NS'))?{return?false;}return?(typeof?window?!==?'undefined'?&&typeof?document?!==?'undefined'); }但是官方已經(jīng)不推薦使用這個(gè)屬性navigator.product。
image.png3.15 forEach 遍歷對(duì)象或數(shù)組
保留了英文注釋,提升大家的英文閱讀能力。
/***?Iterate?over?an?Array?or?an?Object?invoking?a?function?for?each?item.*??用一個(gè)函數(shù)去迭代數(shù)組或?qū)ο?*?If?`obj`?is?an?Array?callback?will?be?called?passing*?the?value,?index,?and?complete?array?for?each?item.*?如果是數(shù)組,回調(diào)將會(huì)調(diào)用value,?index,?和整個(gè)數(shù)組**?If?'obj'?is?an?Object?callback?will?be?called?passing*?the?value,?key,?and?complete?object?for?each?property.*?如果是對(duì)象,回調(diào)將會(huì)調(diào)用value,?key,?和整個(gè)對(duì)象**?@param?{Object|Array}?obj?The?object?to?iterate*?@param?{Function}?fn?The?callback?to?invoke?for?each?item*/function?forEach(obj,?fn)?{//?Don't?bother?if?no?value?provided//?如果值不存在,無(wú)需處理if?(obj?===?null?||?typeof?obj?===?'undefined')?{return;}//?Force?an?array?if?not?already?something?iterable//?如果不是對(duì)象類(lèi)型,強(qiáng)制轉(zhuǎn)成數(shù)組類(lèi)型if?(typeof?obj?!==?'object')?{obj?=?[obj];}if?(isArray(obj))?{//?Iterate?over?array?values//?是數(shù)組,for循環(huán)執(zhí)行回調(diào)fnfor?(var?i?=?0,?l?=?obj.length;?i?<?l;?i++)?{fn.call(null,?obj[i],?i,?obj);}}?else?{//?Iterate?over?object?keys//?是對(duì)象,for循環(huán)執(zhí)行回調(diào)fnfor?(var?key?in?obj)?{//?只遍歷可枚舉屬性if?(Object.prototype.hasOwnProperty.call(obj,?key))?{fn.call(null,?obj[key],?key,?obj);}}} }所以,源碼為什么不用forEach和for...in...呢???????
3.16 stripBOM刪除UTF-8編碼中BOM
/***?Remove?byte?order?marker.?This?catches?EF?BB?BF?(the?UTF-8?BOM)**?@param?{string}?content?with?BOM*?@return?{string}?content?value?without?BOM*/function?stripBOM(content)?{if?(content.charCodeAt(0)?===?0xFEFF)?{content?=?content.slice(1);}return?content; }所謂 BOM,全稱(chēng)是Byte Order Mark,它是一個(gè)Unicode字符,通常出現(xiàn)在文本的開(kāi)頭,用來(lái)標(biāo)識(shí)字節(jié)序。UTF-8主要的優(yōu)點(diǎn)是可以兼容ASCII,但如果使用BOM的話(huà),這個(gè)好處就蕩然無(wú)存了。
4.總結(jié)
本文主要介紹了axios源碼的調(diào)試過(guò)程,以及介紹了一些utils.js中的非常實(shí)用的工具函數(shù);相信通過(guò)閱讀源碼,日積月累,并把這些代碼或思想應(yīng)用的自己項(xiàng)目中去,相信能夠很好的提升自己的編碼能力。
come on! worker!
同時(shí)也推薦一些好用的工具:
瀏覽器中運(yùn)行`vscode`, 查看源碼[7]
代碼沙盒,能運(yùn)行多種語(yǔ)言,且可以添加依賴(lài)[8]
vs code 的 code Runner插件[9]
參考資料
[1]
axios: https://github1s.com/axios/axios
[2]CONTRIBUTING.md: https://github1s.com/axios/axios/blob/HEAD/CONTRIBUTING.md
[3]utils.js: https://github.com/axios/axios/blob/master/lib/utils.js
[4]官方文檔: http://nodejs.cn/api/buffer.html#buffer
[5]更通俗易懂的解釋: https://www.runoob.com/nodejs/nodejs-buffer.html
[6]MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams
[7]瀏覽器中運(yùn)行vscode, 查看源碼: https://github1s.com/axios/axios
[8]代碼沙盒,能運(yùn)行多種語(yǔ)言,且可以添加依賴(lài): https://codesandbox.io/
[9]vs code 的 code Runner插件: https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner
·················?若川簡(jiǎn)介?·················
你好,我是若川,畢業(yè)于江西高校。現(xiàn)在是一名前端開(kāi)發(fā)“工程師”。寫(xiě)有《學(xué)習(xí)源碼整體架構(gòu)系列》20余篇,在知乎、掘金收獲超百萬(wàn)閱讀。
從2014年起,每年都會(huì)寫(xiě)一篇年度總結(jié),已經(jīng)寫(xiě)了7篇,點(diǎn)擊查看年度總結(jié)。
同時(shí),最近組織了源碼共讀活動(dòng),幫助3000+前端人學(xué)會(huì)看源碼。公眾號(hào)愿景:幫助5年內(nèi)前端人走向前列。
識(shí)別上方二維碼加我微信、拉你進(jìn)源碼共讀群
今日話(huà)題
略。分享、收藏、點(diǎn)贊、在看我的文章就是對(duì)我最大的支持~
總結(jié)
以上是生活随笔為你收集整理的axios源码中的10多个工具函数,值得一学~的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 前端学习(3031):vue+eleme
- 下一篇: [html] 如果列表元素li的兄弟元