手写axios核心原理,再也不怕面试官问我axios原理

2023-06-19 0 733

一、axios概要

axios是甚么?

Axios 是两个如前所述 promise 的 HTTP 库,能用在应用程序和 node.js 中。

axios有甚么优点?(不得已说复试被问及数次)

从应用程序中建立 XMLHttpRequests从 node.js 建立 http 允诺全力支持 Promise API截击允诺和积极响应切换允诺统计数据和积极响应统计数据中止允诺手动切换 JSON 统计数据应用程序全力支持防卫 XSRF

事实上,axios能用在应用程序和 node.js 中原因在于,它会手动推论现阶段自然环境是甚么,假如是应用程序,就会如前所述XMLHttpRequests同时实现axios。假如是node.js自然环境,就会如前所述node内建核心理念组件http同时实现axios

单纯而言,axios的基本上基本上原理是

axios却是归属于 XMLHttpRequest, 因而须要同时实现两个ajax。或是如前所述http 。还须要两个promise第一类来对结论展开处置。 有甚么不认知的或是是提议热烈欢迎文章明确提出.工程项目早已放在 github:https://github.com/Sunny-lucking/howToBuildMyAxios

二、基本上采用形式

axios基本上采用形式主要就有

axios(config)axios.method(url, data , config)
// index.html文档 <html> <script type=“text/javascript” src=“axios”></script> <body> <button class=“btn”>点我发送允诺</button> <script> document.querySelector(.btn).onclick = function() { // 分别采用以下方法调用,查看myaxios的效果 axios.post(/postAxios, { name: 小美post }).then(res => { console.log(postAxios 成功积极响应, res); }) axios({ method: post, url: /getAxios }).then(res => { console.log(getAxios 成功积极响应, res); }) } </script> </body> </html> </html>

三、同时实现axios和axios.method

从axios(config)的采用上能看出导出的axios是两个方法。从axios.method(url, data , config)的采用能看出导出的axios上或是原型上挂有get,post等方法

事实上导出的axios是两个Axios类中的两个方法。

如代码所以,核心理念代码是request。我们把request导出,就能采用axios(config)这种形式来调用axios了。

class Axios { constructor() { } request(config) { return new Promise(resolve => { const {url = , method = get, data = {}} = config; // 发送ajax允诺 const xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.onload = function() { console.log(xhr.responseText) resolve(xhr.responseText); } xhr.send(data); }) } }

怎么导出呢?十分单纯,new Axios,获得axios实例,再获得实例上的request方法就好了。

// 最终导出axios的方法,即实例的request方法function CreateAxiosFn() { let axios = new Axios(); let req = axios.request.bind(axios); return req; } // 得到最后的全局变量axioslet axios = CreateAxiosFn();

点击查看此时的myAxios.js

现在axios事实上是request方法。

你可能会很疑惑,因为我当初看源码的时候也很疑惑:干嘛不直接写个request方法,然后导出呢?非得这样绕这么大的弯子。别急。后面慢慢就会讲到。

现在两个单纯的axios就完成了,我们来引入myAxios.js文档并测试一下能采用不?

单纯的搭建服务器:

//server.jsvar express = require(express); var app = express(); //设置允许跨域访问该服务. 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:阳光 }; response.json(data); }); var server = app.listen(5000, function(){ console.log(“服务器启动”); }); //index.html <script type=“text/javascript” src=“./myAxios.js”></script> <body> <button class=“btn”>点我发送允诺</button> <script> document.querySelector(.btn).onclick = function() { // 分别采用以下方法调用,查看myaxios的效果 axios({ method: get, url: http://localhost:5000/getTest }).then(res => { console.log(getAxios 成功积极响应, res); }) } </script> </body>

点击按钮,看看是否能成功获得统计数据。

可喜可贺,成功。

现在我们来同时实现下axios.method()的形式。

思路。我们能再Axios.prototype添加这些方法。而这些方法内部调用request方法即可,如代码所示:

// 定义get,post…方法,挂在到Axios原型上const methodsArr = [get, delete, head, options, put, patch, post]; methodsArr.forEach(met => { Axios.prototype[met] = function() { console.log(执行+met+方法); // 处置单个方法 if ([get, delete, head, options].includes(met)) { // 2个参数(url[, config]) return this.request({ method: met, url: arguments[0], arguments[1] || {} }) } else { // 3个参数(url[,data[,config]]) return this.request({ method: met, url: arguments[0], data: arguments[1] || {}, arguments[2] || {} }) } } })

我们通过遍历methodsArr数组,依次在Axios.prototype添加对应的方法,注意的是get, delete, head, options这些方法只接受两个参数。而其他的可接受三个参数,想一下也知道,get不把参数放body的。

但是,你有没有发现,我们只是在Axios的prototype上添加对应的方法,我们导出去的可是request方法啊,那怎么办? 单纯,把Axios.prototype上的方法搬运到request上即可。

我们先来同时实现两个工具方法,同时实现将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] } } } } }

然后我们就能利用这个方法将Axios.prototype上的方法搬运到request上啦。

我们修改一下之前的CreateAxiosFn方法即可

function CreateAxiosFn() { let axios = new Axios(); let req = axios.request.bind(axios); 增加代码 utils.extend(req, Axios.prototype, axios) return req; }

点击查看此时的myAxios.js

现在来测试一下能不能采用axios.get()这种形式调用axios。

<body> <button class=“btn”>点我发送允诺</button> <script> document.querySelector(.btn).onclick = function() { axios.get(http://localhost:5000/getTest) .then(res => { console.log(getAxios 成功积极响应, res); }) } </script> </body>

害,又是意料之中成功。

再完成下两个功能之前,先给上目前myAxios.js的完整代码

class Axios { constructor() { } request(config) { return new Promise(resolve => { const {url = , method = get, data = {}} = config; // 发送ajax允诺 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(执行+met+方法); // 处置单个方法 if ([get, delete, head, options].includes(met)) { // 2个参数(url[, config]) return this.request({ method: met, url: arguments[0], arguments[1] || {} }) } else { // 3个参数(url[,data[,config]]) return this.request({ method: met, url: arguments[0], data: arguments[1] || {}, arguments[2] || {} }) } } }) // 工具方法,同时实现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] } } } } } // 最终导出axios的方法-》即实例的request方法 function CreateAxiosFn() { let axios = new Axios(); let req = axios.request.bind(axios); // 混入方法, 处置axios的request方法,使之拥有get,post…方法 utils.extend(req, Axios.prototype, axios) return req; } // 得到最后的全局变量axios let axios = CreateAxiosFn();

四、允诺和积极响应截击器

我们先看下截击器的采用

// 添加允诺截击器 axios.interceptors.request.use(function (config) { // 在发送允诺之前做些甚么 return config; }, function (error) { // 对允诺错误做些什么 return Promise.reject(error); }); // 添加积极响应截击器 axios.interceptors.response.use(function (response) { // 对积极响应数据做点甚么 return response; }, function (error) { // 对积极响应错误做点甚么 return Promise.reject(error); });

截击器是甚么意思呢?其实是在我们发送两个允诺的时候会先执行允诺截击器的代码,然后再真正地执行我们发送的允诺,这个过程会对config,也是我们发送允诺时传送的参数展开一些操作。

而当接收积极响应的时候,会先执行积极响应截击器的代码,然后再把积极响应的统计数据返回来,这个过程会对response,也是积极响应的统计数据展开一系列操作。

怎么同时实现呢?须要明确的是截击器也是两个类,管理积极响应和允诺。因而我们先同时实现截击器

class InterceptorsManage { constructor() { this.handlers = []; } use(fullfield, rejected) { this.handlers.push({ fullfield, rejected }) } }

我们是用这个语句axios.interceptors.response.use和axios.interceptors.request.use,来触发截击器执行use方法的。

说明axios上有两个积极响应截击器和两个允诺截击器。那怎么同时实现Axios呢?看代码

class Axios { constructor() { 新增代码 this.interceptors = { request: new InterceptorsManage, response: new InterceptorsManage } } request(config) { return new Promise(resolve => { const {url = , method = get, data = {}} = config; // 发送ajax允诺 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实例上有两个第一类interceptors。这个第一类有两个截击器,两个用来处置允诺,两个用来处置积极响应。

所以,我们执行语句axios.interceptors.response.use和a法。

而执行use方法,会把我们传入的回调函数push到截击器的handlers数组里。

到这里你有没有发现两个问题。这个interceptors第一类是Axios上的啊,我们导出的是request方法啊(欸?好熟悉的问题,上面提到过哈哈哈~~~额)。处置方法跟上面处置的形式一样,都是把Axios上的方法和属性搬到request过去,也是遍历Axios实例上的方法,得以将interceptors第一类挂载到request上。

所以只要更改下CreateAxiosFn方法即可。

function CreateAxiosFn() { let axios = new Axios(); let req = axios.request.bind(axios); // 混入方法, 处置axios的request方法,使之拥有get,post…方法 utils.extend(req, Axios.prototype, axios) 新增代码 utils.extend(req, axios) return req; }

好了,现在request也有了interceptors第一类,那么甚么时候拿interceptors第一类中的handler之前保存的回调函数出来执行。

因而,我们要修改之前所写的request方法 之前是这样的。

request(config) { return new Promise(resolve => { const {url = , method = get, data = {}} = config; // 发送ajax允诺 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); }) }

但是现在request里不仅要执行发送ajax允诺,还要执行截击器handlers中的回调函数。所以,最好下是将执行ajax的允诺封装成两个方法

request(config) { this.sendAjax(config) } sendAjax(config){ return new Promise(resolve => { const {url = , method = get, data = {}} = config; // 发送ajax允诺 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); }) }

好了,现在我们要获得handlers中的回调

request(config) { // 截击器和允诺组装队列 let chain = [this.sendAjax.bind(this), undefined] // 成对出现的,失败回调暂时不处置 // 允诺截击 this.interceptors.request.handlers.forEach(interceptor => { chain.unshift(interceptor.fullfield, interceptor.rejected) }) // 积极响应截击 this.interceptors.response.handlers.forEach(interceptor => { chain.push(interceptor.fullfield, interceptor.rejected) }) // 执行队列,每次执行一对,并给promise赋最新的值 let promise = Promise.resolve(config); while(chain.length > 0) { promise = promise.then(chain.shift(), chain.shift()) } return promise; }

我们先把sendAjax允诺和undefined放进了chain数组里,再把允诺截击器的handlers的成对回调放在chain数组头部。再把积极响应截击器的handlers的承兑回调反倒chain数组的尾部。

然后再 逐渐取数 chain数组的成对回调执行。

promise = promise.then(chain.shift(), chain.shift())

这一句,事实上是不断将config从上两个promise传递到下两个promise,期间可能回调config做出一些修改。甚么意思?我们结合两个例子来讲解一下

首先截击器是这样采用的

// 添加允诺截击器 axios.interceptors.request.use(function (config) { // 在发送允诺之前做些甚么 return config; }, function (error) { // 对允诺错误做些甚么 return Promise.reject(error); }); // 添加积极响应截击器 axios.interceptors.response.use(function (response) { // 对积极响应统计数据做点甚么 return response; }, function (error) { // 对积极响应错误做点甚么 return Promise.reject(error); });

然后执行request的时候。chain数组的统计数据是这样的

chain = [ function (config) { // 在发送允诺之前做些甚么 return config; }, function (error) { // 对允诺错误做些甚么 return Promise.reject(error); } this.sendAjax.bind(this), undefined, function (response) { // 对积极响应统计数据做点甚么 return response; }, function (error) { // 对积极响应错误做点甚么 return Promise.reject(error); } ]

首先

执行第一次promise.then(chain.shift(), chain.shift()),即

promise.then( function (config) { // 在发送允诺之前做些甚么 return config; }, function (error) { // 对允诺错误做些甚么 return Promise.reject(error); } )

一般情况,promise是resolved状态,是执行成功回调的,也是执行

function (config) { // 在发送允诺之前做些甚么 return config; },

而promise.then是要返回两个新的promise第一类的。

为了区分,在这里,我会把这个新的promise第一类叫做第两个新的promise第一类 这个第两个新的promise第一类会把

function (config) { // 在发送允诺之前做些甚么 return config; },

的执行结论传入resolve函数中

resolve(config)

使得这个返回的第两个新的promise第一类的状态为resovled,而且第两个新的promise第一类的data为config。

这里须要对Promise的基本上原理足够认知。所以我前一篇文章写的是记事本Promise核心理念基本上原理,非但怕辩手问我Promise基本上原理,你能去看看 接下来,再执行

promise.then( sendAjax(config) , undefined )

注意:这里的promise是 上面提到的第两个新的promise第一类。

而promise.then这个的执行又会返回第二个新的promise第一类。

因为这里promise.then中的promise也是第两个新的promise第一类的状态是resolved的,所以会执行sendAjax()。而且会取出第两个新的promise第一类的data 作为config转入sendAjax()。

当sendAjax执行完,就会返回两个response。这个response就会保存在第二个新的promise第一类的data中。

接下来,再执行

promise.then( function (response) { // 对积极响应统计数据做点甚么 return response; }, function (error) { // 对积极响应错误做点甚么 return Promise.reject(error); } )

同样,会把第二个新的promise第一类的data取出来作为response参数传入

function (response) { // 对积极响应统计数据做点甚么 return response; },

饭后返回两个promise第一类,这个promise第一类的data保存了这个函数的执行结论,也是返回值response。

然后通过return promise;

把这个promise返回了。咦?是怎么取出promise的data的。我们看看我们平常事怎么获得积极响应统计数据的

axios.get(http://localhost:5000/getTest) .then(res => { console.log(getAxios 成功积极响应, res); })

在then里接收积极响应统计数据。所以基本上原理跟上面一样,将返回的promise的data作为res参数了。

现在看看我们的myAxios完整代码吧,好有个全面的了解

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) { // 截击器和允诺组装队列 let chain = [this.sendAjax.bind(this), undefined] // 成对出现的,失败回调暂时不处理 // 允诺截击 this.interceptors.request.handlers.forEach(interceptor => { chain.unshift(interceptor.fullfield, interceptor.rejected) }) // 积极响应截击 this.interceptors.response.handlers.forEach(interceptor => { chain.push(interceptor.fullfield, interceptor.rejected) }) // 执行队列,每次执行一对,并给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; // 发送ajax允诺 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(执行+met+方法); // 处置单个方法 if ([get, delete, head, options].includes(met)) { // 2个参数(url[, config]) return this.request({ method: met, url: arguments[0], arguments[1] || {} }) } else { // 3个参数(url[,data[,config]]) return this.request({ method: met, url: arguments[0], data: arguments[1] || {}, arguments[2] || {} }) } } }) // 工具方法,同时实现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] } } } } } // 最终导出axios的方法-》即实例的request方法function CreateAxiosFn() { let axios = new Axios(); let req = axios.request.bind(axios); // 混入方法, 处置axios的request方法,使之拥有get,post…方法 utils.extend(req, Axios.prototype, axios) return req; } // 得到最后的全局变量axios let axios = CreateAxiosFn();

来测试下截击器功能是否正常

<script type=“text/javascript” src=“./myAxios.js”></script> <body> <button class=“btn”>点我发送允诺</button> <script> // 添加允诺截击器 axios.interceptors.request.use(function (config) { // 在发送允诺之前做些甚么 config.method = “get”; console.log(“被我允诺截击器截击了,哈哈:”,config); return config; }, function (error) { // 对允诺错误做些甚么 return Promise.reject(error); }); // 添加积极响应截击器 axios.interceptors.response.use(function (response) { // 对积极响应统计数据做点甚么 console.log(“被我积极响应截击截击了,哈哈 “); response = {message:“积极响应统计数据被我替换了,啊哈哈哈”} return response; }, function (error) { // 对积极响应错误做点甚么 console.log(“错了吗”); return Promise.reject(error); }); document.querySelector(.btn).onclick = function() { // 分别采用以下方法调用,查看myaxios的效果 axios({ url: http://localhost:5000/getTest }).then(res => { console.log(response, res); }) } </script> </body>

截击成功!!!!!全掘金人民向我们发来贺电!!!!

有甚么不认知的或是是提议热烈欢迎文章明确提出

感谢您也恭喜您看到这里,我能卑微的求个star吗!!!

github:https://github.com/Sunny-lucking/howToBuildMyAxios

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务