手寫Axios核心原理,再也不怕面試官問(wèn)我Axios原理
手寫axios核心原理
- 手寫axios核心原理,再也不怕面試官問(wèn)我axios原理
- 手寫axios核心原理
一、axios簡(jiǎn)介
- axios是什么?
- axios有什么特性?(不得不說(shuō)面試被問(wèn)到幾次)
二、基本使用方式
三、實(shí)現(xiàn)axios和axios.method
四、請(qǐng)求和響應(yīng)攔截器
- 首先
- 接下來(lái),再執(zhí)行
- 接下來(lái),再執(zhí)行
一、axios簡(jiǎn)介
axios是什么?
Axios 是一個(gè)基于 promise 的 HTTP 庫(kù),可以用在瀏覽器和 node.js 中。
axios有什么特性?(不得不說(shuō)面試被問(wèn)到幾次)
- 從瀏覽器中創(chuàng)建 XMLHttpRequests從 node.js
- 創(chuàng)建 http 請(qǐng)求
- 支持 Promise API
- 攔截請(qǐng)求和響應(yīng)轉(zhuǎn)換請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù)
- 取消請(qǐng)求
- 自動(dòng)轉(zhuǎn)換JSON 數(shù)據(jù)
- 客戶端支持防御 XSRF
實(shí)際上,axios可以用在瀏覽器和 node.js 中是因?yàn)椋鼤?huì)自動(dòng)判斷當(dāng)前環(huán)境是什么,如果是瀏覽器,就會(huì)基于XMLHttpRequests實(shí)現(xiàn)axios。如果是node.js環(huán)境,就會(huì)基于node內(nèi)置核心模塊http實(shí)現(xiàn)axios簡(jiǎn)單來(lái)說(shuō),axios的基本原理就是
- axios還是屬于 XMLHttpRequest, 因此需要實(shí)現(xiàn)一個(gè)ajax。或者基于http 。
- 還需要一個(gè)promise對(duì)象來(lái)對(duì)結(jié)果進(jìn)行處理。
有什么不理解的或者是建議歡迎評(píng)論提出.項(xiàng)目已經(jīng)放到github.可以的話給個(gè)star吧!謝謝 github:https://github.com/Sunny-lucking/howToBuildMyAxios
二、基本使用方式
axios基本使用方式主要有
- axios(config)
- axios.method(url, data , config)
- // index.html文件
- <html>
- <script type="text/javascript" src="./myaxios.js"></script>
- <body>
- <button class="btn">點(diǎn)我發(fā)送請(qǐng)求</button>
- <script>
- document.querySelector('.btn').onclick = function() {
- // 分別使用以下方法調(diào)用,查看myaxios的效果
- axios.post('/postAxios', {
- name: '小美post'
- }).then(res => {
- console.log('postAxios 成功響應(yīng)', res);
- })
- axios({
- method: 'post',
- url: '/getAxios'
- }).then(res => {
- console.log('getAxios 成功響應(yīng)', res);
- })
- }
- </script>
- </body>
- </html>
- </html>
三、實(shí)現(xiàn)axios和axios.method
從axios(config)的使用上可以看出導(dǎo)出的axios是一個(gè)方法。從axios.method(url, data , config)的使用可以看出導(dǎo)出的axios上或者原型上掛有g(shù)et,post等方法。
實(shí)際上導(dǎo)出的axios就是一個(gè)Axios類中的一個(gè)方法。
如代碼所以,核心代碼是request。我們把request導(dǎo)出,就可以使用axios(config)這種形式來(lái)調(diào)用axios了。
- class Axios {
- constructor() {
- }
- request(config) {
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- }
- xhr.send(data);
- })
- }
- }
怎么導(dǎo)出呢?十分簡(jiǎn)單,new Axios,獲得axios實(shí)例,再獲得實(shí)例上的request方法就好了。
- // 最終導(dǎo)出axios的方法,即實(shí)例的request方法
- function CreateAxiosFn() {
- let axios = new Axios();
- let req = axios.request.bind(axios);
- return req;
- }
- // 得到最后的全局變量axios
- let axios = CreateAxiosFn();
點(diǎn)擊查看此時(shí)的myAxios.js
現(xiàn)在axios實(shí)際上就是request方法。
你可能會(huì)很疑惑,因?yàn)槲耶?dāng)初看源碼的時(shí)候也很疑惑:干嘛不直接寫個(gè)request方法,然后導(dǎo)出呢?非得這樣繞這么大的彎子。別急。后面慢慢就會(huì)講到。
現(xiàn)在一個(gè)簡(jiǎn)單的axios就完成了,我們來(lái)引入myAxios.js文件并測(cè)試一下可以使用不?
簡(jiǎn)單的搭建服務(wù)器:
- //server.js
- var express = require('express');
- var app = express();
- //設(shè)置允許跨域訪問(wèn)該服務(wù).
- app.all('*', function (req, res, next) {
- res.header('Access-Control-Allow-Origin', '*');
- res.header('Access-Control-Allow-Headers', 'Content-Type');
- res.header('Access-Control-Allow-Methods', '*');
- res.header('Content-Type', 'application/json;charset=utf-8');
- next();
- });
- app.get('/getTest', function(request, response){
- data = {
- 'FrontEnd':'前端',
- 'Sunny':'陽(yáng)光'
- };
- response.json(data);
- });
- var server = app.listen(5000, function(){
- console.log("服務(wù)器啟動(dòng)");
- });
- //index.html
- <script type="text/javascript" src="./myAxios.js"></script>
- <body>
- <button class="btn">點(diǎn)我發(fā)送請(qǐng)求</button>
- <script>
- document.querySelector('.btn').onclick = function() {
- // 分別使用以下方法調(diào)用,查看myaxios的效果
- axios({
- method: 'get',
- url: 'http://localhost:5000/getTest'
- }).then(res => {
- console.log('getAxios 成功響應(yīng)', res);
- })
- }
- </script>
- </body>
點(diǎn)擊按鈕,看看是否能成功獲得數(shù)據(jù)。
發(fā)現(xiàn)確實(shí)成功。
可喜可賀
現(xiàn)在我們來(lái)實(shí)現(xiàn)下axios.method()的形式。
思路:我們可以再Axios.prototype添加這些方法。而這些方法內(nèi)部調(diào)用request方法即可,如代碼所示:
- // 定義get,post...方法,掛在到Axios原型上
- const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
- methodsArr.forEach(met => {
- Axios.prototype[met] = function() {
- console.log('執(zhí)行'+met+'方法');
- // 處理單個(gè)方法
- if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個(gè)參數(shù)(url[, config])
- return this.request({
- method: met,
- url: arguments[0],
- ...arguments[1] || {}
- })
- } else { // 3個(gè)參數(shù)(url[,data[,config]])
- return this.request({
- method: met,
- url: arguments[0],
- data: arguments[1] || {},
- ...arguments[2] || {}
- })
- }
- }
- })
我們通過(guò)遍歷methodsArr數(shù)組,依次在Axios.prototype添加對(duì)應(yīng)的方法,注意的是'get', 'delete', 'head', 'options'這些方法只接受兩個(gè)參數(shù)。而其他的可接受三個(gè)參數(shù),想一下也知道,get不把參數(shù)放body的。
但是,你有沒(méi)有發(fā)現(xiàn),我們只是在Axios的prototype上添加對(duì)應(yīng)的方法,我們導(dǎo)出去的可是request方法啊,那怎么辦?簡(jiǎn)單,把Axios.prototype上的方法搬運(yùn)到request上即可。
我們先來(lái)實(shí)現(xiàn)一個(gè)工具方法,實(shí)現(xiàn)將b的方法混入a;
- const utils = {
- extend(a,b, context) {
- for(let key in b) {
- if (b.hasOwnProperty(key)) {
- if (typeof b[key] === 'function') {
- a[key] = b[key].bind(context);
- } else {
- a[key] = b[key]
- }
- }
- }
- }
- }
然后我們就可以利用這個(gè)方法將Axios.prototype上的方法搬運(yùn)到request上啦。
我們修改一下之前的CreateAxiosFn方法即可
- function CreateAxiosFn() {
- let axios = new Axios();
- let req = axios.request.bind(axios);
- 增加代碼
- utils.extend(req, Axios.prototype, axios)
- return req;
- }
點(diǎn)擊查看此時(shí)的myAxios.js
現(xiàn)在來(lái)測(cè)試一下能不能使用axios.get()這種形式調(diào)用axios。
- <body>
- <button class="btn">點(diǎn)我發(fā)送請(qǐng)求</button>
- <script>
- document.querySelector('.btn').onclick = function() {
- axios.get('http://localhost:5000/getTest')
- .then(res => {
- console.log('getAxios 成功響應(yīng)', res);
- })
- }
- </script>
- </body>
又是意料之中成功。再完成下一個(gè)功能之前,先給上目前myAxios.js的完整代碼
- class Axios {
- constructor() {
- }
- request(config) {
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- console.log(config);
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- }
- xhr.send(data);
- })
- }
- }
- // 定義get,post...方法,掛在到Axios原型上
- const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
- methodsArr.forEach(met => {
- Axios.prototype[met] = function() {
- console.log('執(zhí)行'+met+'方法');
- // 處理單個(gè)方法
- if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個(gè)參數(shù)(url[, config])
- return this.request({
- method: met,
- url: arguments[0],
- ...arguments[1] || {}
- })
- } else { // 3個(gè)參數(shù)(url[,data[,config]])
- return this.request({
- method: met,
- url: arguments[0],
- data: arguments[1] || {},
- ...arguments[2] || {}
- })
- }
- }
- })
- // 工具方法,實(shí)現(xiàn)b的方法或?qū)傩曰烊隺;
- // 方法也要混入進(jìn)去
- const utils = {
- extend(a,b, context) {
- for(let key in b) {
- if (b.hasOwnProperty(key)) {
- if (typeof b[key] === 'function') {
- a[key] = b[key].bind(context);
- } else {
- a[key] = b[key]
- }
- }
- }
- }
- }
- // 最終導(dǎo)出axios的方法-》即實(shí)例的request方法
- function CreateAxiosFn() {
- let axios = new Axios();
- let req = axios.request.bind(axios);
- // 混入方法, 處理axios的request方法,使之擁有g(shù)et,post...方法
- utils.extend(req, Axios.prototype, axios)
- return req;
- }
- // 得到最后的全局變量axios
- let axios = CreateAxiosFn();
四、請(qǐng)求和響應(yīng)攔截器
我們先看下攔截器的使用
- // 添加請(qǐng)求攔截器
- axios.interceptors.request.use(function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- }, function (error) {
- // 對(duì)請(qǐng)求錯(cuò)誤做些什么
- return Promise.reject(error);
- });
- // 添加響應(yīng)攔截器
- axios.interceptors.response.use(function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- return response;
- }, function (error) {
- // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
- return Promise.reject(error);
- });
攔截器是什么意思呢?其實(shí)就是在我們發(fā)送一個(gè)請(qǐng)求的時(shí)候會(huì)先執(zhí)行請(qǐng)求攔截器的代碼,然后再真正地執(zhí)行我們發(fā)送的請(qǐng)求,這個(gè)過(guò)程會(huì)對(duì)config,也就是我們發(fā)送請(qǐng)求時(shí)傳送的參數(shù)進(jìn)行一些操作。
而當(dāng)接收響應(yīng)的時(shí)候,會(huì)先執(zhí)行響應(yīng)攔截器的代碼,然后再把響應(yīng)的數(shù)據(jù)返回來(lái),這個(gè)過(guò)程會(huì)對(duì)response,也就是響應(yīng)的數(shù)據(jù)進(jìn)行一系列操作。
怎么實(shí)現(xiàn)呢?需要明確的是攔截器也是一個(gè)類,管理響應(yīng)和請(qǐng)求。因此我們先實(shí)現(xiàn)攔截器
- class InterceptorsManage {
- constructor() {
- this.handlers = [];
- }
- use(fullfield, rejected) {
- this.handlers.push({
- fullfield,
- rejected
- })
- }
- }
我們是用這個(gè)語(yǔ)句axios.interceptors.response.use和axios.interceptors.request.use,來(lái)觸發(fā)攔截器執(zhí)行use方法的。
說(shuō)明axios上有一個(gè)響應(yīng)攔截器和一個(gè)請(qǐng)求攔截器。那怎么實(shí)現(xiàn)Axios呢?看代碼
- class Axios {
- constructor() {
- 新增代碼
- this.interceptors = {
- request: new InterceptorsManage,
- response: new InterceptorsManage
- }
- }
- request(config) {
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- console.log(config);
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- };
- xhr.send(data);
- })
- }
- }
可見,axios實(shí)例上有一個(gè)對(duì)象interceptors。這個(gè)對(duì)象有兩個(gè)攔截器,一個(gè)用來(lái)處理請(qǐng)求,一個(gè)用來(lái)處理響應(yīng)。
所以,我們執(zhí)行語(yǔ)句axios.interceptors.response.use和axios.interceptors.request.use的時(shí)候,實(shí)現(xiàn)獲取axios實(shí)例上的interceptors對(duì)象,然后再獲取response或request攔截器,再執(zhí)行對(duì)應(yīng)的攔截器的use方法。
而執(zhí)行use方法,會(huì)把我們傳入的回調(diào)函數(shù)push到攔截器的handlers數(shù)組里。
到這里你有沒(méi)有發(fā)現(xiàn)一個(gè)問(wèn)題。這個(gè)interceptors對(duì)象是Axios上的啊,我們導(dǎo)出的是request方法啊(欸?好熟悉的問(wèn)題,上面提到過(guò)哈哈哈~~~額)。處理方法跟上面處理的方式一樣,都是把Axios上的方法和屬性搬到request過(guò)去,也就是遍歷Axios實(shí)例上的方法,得以將interceptors對(duì)象掛載到request上。
所以只要更改下CreateAxiosFn方法即可。
- function CreateAxiosFn() {
- let axios = new Axios();
- let req = axios.request.bind(axios);
- // 混入方法, 處理axios的request方法,使之擁有g(shù)et,post...方法
- utils.extend(req, Axios.prototype, axios)
- 新增代碼
- utils.extend(req, axios)
- return req;
- }
好了,現(xiàn)在request也有了interceptors對(duì)象,那么什么時(shí)候拿interceptors對(duì)象中的handler之前保存的回調(diào)函數(shù)出來(lái)執(zhí)行。
沒(méi)錯(cuò),就是我們發(fā)送請(qǐng)求的時(shí)候,會(huì)先獲取request攔截器的handlers的方法來(lái)執(zhí)行。再執(zhí)行我們發(fā)送的請(qǐng)求,然后獲取response攔截器的handlers的方法來(lái)執(zhí)行。
因此,我們要修改之前所寫的request方法 之前是這樣的。
- request(config) {
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- console.log(config);
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- };
- xhr.send(data);
- })
- }
但是現(xiàn)在request里不僅要執(zhí)行發(fā)送ajax請(qǐng)求,還要執(zhí)行攔截器handlers中的回調(diào)函數(shù)。所以,最好下就是將執(zhí)行ajax的請(qǐng)求封裝成一個(gè)方法
- request(config) {
- this.sendAjax(config)
- }
- sendAjax(config){
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- console.log(config);
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- };
- xhr.send(data);
- })
- }
好了,現(xiàn)在我們要獲得handlers中的回調(diào)
- request(config) {
- // 攔截器和請(qǐng)求組裝隊(duì)列
- let chain = [this.sendAjax.bind(this), undefined] // 成對(duì)出現(xiàn)的,失敗回調(diào)暫時(shí)不處理
- // 請(qǐng)求攔截
- this.interceptors.request.handlers.forEach(interceptor => {
- chain.unshift(interceptor.fullfield, interceptor.rejected)
- })
- // 響應(yīng)攔截
- this.interceptors.response.handlers.forEach(interceptor => {
- chain.push(interceptor.fullfield, interceptor.rejected)
- })
- // 執(zhí)行隊(duì)列,每次執(zhí)行一對(duì),并給promise賦最新的值
- let promise = Promise.resolve(config);
- while(chain.length > 0) {
- promise = promise.then(chain.shift(), chain.shift())
- }
- return promise;
- }
我們先把sendAjax請(qǐng)求和undefined放進(jìn)了chain數(shù)組里,再把請(qǐng)求攔截器的handlers的成對(duì)回調(diào)放到chain數(shù)組頭部。再把響應(yīng)攔截器的handlers的承兌回調(diào)反倒chain數(shù)組的尾部。
然后再 逐漸取數(shù) chain數(shù)組的成對(duì)回調(diào)執(zhí)行。
- promise = promise.then(chain.shift(), chain.shift())
這一句,實(shí)際上就是不斷將config從上一個(gè)promise傳遞到下一個(gè)promise,期間可能回調(diào)config做出一些修改。什么意思?我們結(jié)合一個(gè)例子來(lái)講解一下
首先攔截器是這樣使用的
- // 添加請(qǐng)求攔截器
- axios.interceptors.request.use(function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- }, function (error) {
- // 對(duì)請(qǐng)求錯(cuò)誤做些什么
- return Promise.reject(error);
- });
- // 添加響應(yīng)攔截器
- axios.interceptors.response.use(function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- return response;
- }, function (error) {
- // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
- return Promise.reject(error);
- });
然后執(zhí)行request的時(shí)候。chain數(shù)組的數(shù)據(jù)是這樣的
- chain = [
- function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- },
- function (error) {
- // 對(duì)請(qǐng)求錯(cuò)誤做些什么
- return Promise.reject(error);
- }
- this.sendAjax.bind(this),
- undefined,
- function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- return response;
- },
- function (error) {
- // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
- return Promise.reject(error);
- }
- ]
首先
執(zhí)行第一次promise.then(chain.shift(), chain.shift()),即
- promise.then(
- function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- },
- function (error) {
- // 對(duì)請(qǐng)求錯(cuò)誤做些什么
- return Promise.reject(error);
- }
- )
一般情況,promise是resolved狀態(tài),是執(zhí)行成功回調(diào)的,也就是執(zhí)行
- function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- },
promise.then是要返回一個(gè)新的promise對(duì)象的。為了區(qū)分,在這里,我會(huì)把這個(gè)新的promise對(duì)象叫做第一個(gè)新的promise對(duì)象 這個(gè)第一個(gè)新的promise對(duì)象會(huì)把
- function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- return config;
- },
的執(zhí)行結(jié)果傳入resolve函數(shù)中
- resolve(config)
使得這個(gè)返回的第一個(gè)新的promise對(duì)象的狀態(tài)為resovled,而且第一個(gè)新的promise對(duì)象的data為config。
這里需要對(duì)Promise的原理足夠理解。所以我前一篇文章寫的是手寫Promise核心原理,再也不怕面試官問(wèn)我Promise原理,你可以去看看
接下來(lái),再執(zhí)行
- promise.then(
- sendAjax(config)
- ,
- undefined
- )
注意:這里的promise是 上面提到的第一個(gè)新的promise對(duì)象。
而promise.then這個(gè)的執(zhí)行又會(huì)返回第二個(gè)新的promise對(duì)象。
因?yàn)檫@里promise.then中的promise也就是第一個(gè)新的promise對(duì)象的狀態(tài)是resolved的,所以會(huì)執(zhí)行sendAjax()。而且會(huì)取出第一個(gè)新的promise對(duì)象的data 作為config轉(zhuǎn)入sendAjax()。
當(dāng)sendAjax執(zhí)行完,就會(huì)返回一個(gè)response。這個(gè)response就會(huì)保存在第二個(gè)新的promise對(duì)象的data中。
接下來(lái),再執(zhí)行
- promise.then(
- function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- return response;
- },
- function (error) {
- // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
- return Promise.reject(error);
- }
- )
同樣,會(huì)把第二個(gè)新的promise對(duì)象的data取出來(lái)作為response參數(shù)傳入
- function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- return response;
- },
飯后返回一個(gè)promise對(duì)象,這個(gè)promise對(duì)象的data保存了這個(gè)函數(shù)的執(zhí)行結(jié)果,也就是返回值response。
然后通過(guò)return promise;
把這個(gè)promise返回了。咦?是怎么取出promise的data的。我們看看我們平常事怎么獲得響應(yīng)數(shù)據(jù)的
- axios.get('http://localhost:5000/getTest')
- .then(res => {
- console.log('getAxios 成功響應(yīng)', res);
- })
在then里接收響應(yīng)數(shù)據(jù)。所以原理跟上面一樣,將返回的promise的data作為res參數(shù)了。
現(xiàn)在看看我們的myAxios完整代碼吧,好有個(gè)全面的了解
- class InterceptorsManage {
- constructor() {
- this.handlers = [];
- }
- use(fullfield, rejected) {
- this.handlers.push({
- fullfield,
- rejected
- })
- }
- }
- class Axios {
- constructor() {
- this.interceptors = {
- request: new InterceptorsManage,
- response: new InterceptorsManage
- }
- }
- request(config) {
- // 攔截器和請(qǐng)求組裝隊(duì)列
- let chain = [this.sendAjax.bind(this), undefined] // 成對(duì)出現(xiàn)的,失敗回調(diào)暫時(shí)不處理
- // 請(qǐng)求攔截
- this.interceptors.request.handlers.forEach(interceptor => {
- chain.unshift(interceptor.fullfield, interceptor.rejected)
- })
- // 響應(yīng)攔截
- this.interceptors.response.handlers.forEach(interceptor => {
- chain.push(interceptor.fullfield, interceptor.rejected)
- })
- // 執(zhí)行隊(duì)列,每次執(zhí)行一對(duì),并給promise賦最新的值
- let promise = Promise.resolve(config);
- while(chain.length > 0) {
- promise = promise.then(chain.shift(), chain.shift())
- }
- return promise;
- }
- sendAjax(){
- return new Promise(resolve => {
- const {url = '', method = 'get', data = {}} = config;
- // 發(fā)送ajax請(qǐng)求
- console.log(config);
- const xhr = new XMLHttpRequest();
- xhr.open(method, url, true);
- xhr.onload = function() {
- console.log(xhr.responseText)
- resolve(xhr.responseText);
- };
- xhr.send(data);
- })
- }
- }
- // 定義get,post...方法,掛在到Axios原型上
- const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
- methodsArr.forEach(met => {
- Axios.prototype[met] = function() {
- console.log('執(zhí)行'+met+'方法');
- // 處理單個(gè)方法
- if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個(gè)參數(shù)(url[, config])
- return this.request({
- method: met,
- url: arguments[0],
- ...arguments[1] || {}
- })
- } else { // 3個(gè)參數(shù)(url[,data[,config]])
- return this.request({
- method: met,
- url: arguments[0],
- data: arguments[1] || {},
- ...arguments[2] || {}
- })
- }
- }
- })
- // 工具方法,實(shí)現(xiàn)b的方法混入a;
- // 方法也要混入進(jìn)去
- const utils = {
- extend(a,b, context) {
- for(let key in b) {
- if (b.hasOwnProperty(key)) {
- if (typeof b[key] === 'function') {
- a[key] = b[key].bind(context);
- } else {
- a[key] = b[key]
- }
- }
- }
- }
- }
- // 最終導(dǎo)出axios的方法-》即實(shí)例的request方法
- function CreateAxiosFn() {
- let axios = new Axios();
- let req = axios.request.bind(axios);
- // 混入方法, 處理axios的request方法,使之擁有g(shù)et,post...方法
- utils.extend(req, Axios.prototype, axios)
- return req;
- }
- // 得到最后的全局變量axios
- let axios = CreateAxiosFn();
來(lái)測(cè)試下攔截器功能是否正常
- <script type="text/javascript" src="./myAxios.js"></script>
- <body>
- <button class="btn">點(diǎn)我發(fā)送請(qǐng)求</button>
- <script>
- // 添加請(qǐng)求攔截器
- axios.interceptors.request.use(function (config) {
- // 在發(fā)送請(qǐng)求之前做些什么
- config.method = "get";
- console.log("被我請(qǐng)求攔截器攔截了,哈哈:",config);
- return config;
- }, function (error) {
- // 對(duì)請(qǐng)求錯(cuò)誤做些什么
- return Promise.reject(error);
- });
- // 添加響應(yīng)攔截器
- axios.interceptors.response.use(function (response) {
- // 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
- console.log("被我響應(yīng)攔截?cái)r截了,哈哈 ");
- response = {message:"響應(yīng)數(shù)據(jù)被我替換了,啊哈哈哈"}
- return response;
- }, function (error) {
- // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
- console.log("錯(cuò)了嗎");
- return Promise.reject(error);
- });
- document.querySelector('.btn').onclick = function() {
- // 分別使用以下方法調(diào)用,查看myaxios的效果
- axios({
- url: 'http://localhost:5000/getTest'
- }).then(res => {
- console.log('response', res);
- })
- }
- </script>
- </body>
攔截成功?。。。?!