Axios是一个基于promise的HTTP库,它能够自动判断当前环境,自由切换在浏览器和 node.js环境中。如果是浏览器,就会基于XMLHttpRequests实现;如果是node环境,就会基于node内置核心http模块实现。同时,它还用promise处理了响应结果,避免陷入回调地狱中去。

不仅如此,Axios还可以拦截请求和响应、转化请求数据和响应数据、中断请求、自动转换JSON数据、客户端支持防御XSRF等。如此众多好用的功能,快来一起看看它是如何实现的吧!
1.基本使用
axios基本使用方式主要有:
- // 发送 POST 请求
 - axios({
 - method: 'post',
 - url: '/user/12345',
 - data: {
 - username: 'Web前端严选',
 - age: 2
 - }
 - });
 - // GET请求ID参数
 - axios.get('/user?ID=12345')
 - .then(function (response) {
 - console.log(response);
 - })
 - .catch(function (error) {
 - console.log(error);
 - });
 
2.实现axios
从axios(config)的使用上可以看出导出的axios是一个方法,从axios.get()的使用上可以看出导出的axios原型上会有get,post,put,delete等方法。
由分析可知,axios实际上是Axios类中的一个方法。我们可以先写一个request方法来实现主要的请求功能,这样就能使用axios(config)形式来调用了。
- class Axios{
 - constructor(){
 - }
 - request(config){
 - return new Promise((resolve) => {
 - const {url='',data={},method='get'} = config; //解构传参
 - const xhr = new XMLHttpRequest; //创建请求对象
 - xhr.open(method,url,true);
 - xhr.onreadystatechange = () => {
 - if(xhr.readyState == 4 && xhr.status == 200){
 - resolve(xhr.responseText);
 - //异步请求返回后将Promise转为成功态并将结果导出
 - }
 - }
 - xhr.send(JSON.stringfy(data));
 - })
 - }
 - }
 - function CreateAxiosFn(){
 - let axios = new Axios;
 - let req = axios.request.bind(axios);
 - return req;
 - }
 - let axios = CreateAxiosFn();
 
然后搭建一个简易服务端代码,以测试请求的效果:
- const express = require('express')
 - let 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('/getInfo', function(request, response){
 - let data = {
 - 'username':'前端严选',
 - 'age':'2'
 - };
 - response.json(data);
 - });
 - app.listen(3000, function(){
 - console.log("服务器启动");
 - });
 
启动服务后,在页面中测试请求是否成功:
- function getMsg(){
 - axios({
 - method: 'get',
 - url: 'http://localhost:3000/getInfo'
 - }).then(res => {
 - console.log(res);
 - })
 - }
 
点击按钮后,可以看到请求成功并获取到数据。
3.原型上的方法
接下来实现以axios.method()形式的方法。
通过axios.get(),axios.post(),axios.put()等方法可以看出它们都是Axios.prototype上的方法,这些方法调用内部的request方法即可:
- const methodsArr = ['get','post','put','delete','head','options','patch','head'];
 - methodsArr.forEach(method => {
 - Axios.prototype[method] = function(){
 - return this.request({
 - method: method,
 - ...arguments[0]
 - })
 - }
 - })
 
arguments的第一个参数包含url,data等信息,直接解构它的第一个元素即可
还需要实现一个工具方法,用来将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方法,使之拥有get,post等方法
- function CreateAxiosFn(){
 - let axios = new Axios;
 - let req = axios.request.bind(axios);
 - //新增如下代码
 - utils.extend(req, Axios.prototype, axios)
 - return req;
 - }
 
再来测试一下post的请求:
- axios.post({
 - url: 'http://localhost:3000/postTest',
 - data: {
 - a: 1,
 - b: 2
 - }
 - }).then(res => {
 - console.log(res);
 - })
 
可以看到正确返回结果了。
4.拦截器
先来看看拦截器的使用:
- // 请求拦截
 - 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);
 - });
 
拦截器,顾名思义就是在请求之前和响应之前,对真正要执行的操作数据拦截住进行一些处理。
那么如何实现呢,首先拦截器也是一个类,用于管理响应和请求。
- class InterceptorsManage{
 - constructor(){
 - this.handlers = [];
 - }
 - use(onFulField,onRejected){
 - //将成功的回调和失败的回调都存放到队列中
 - this.handlers.push({
 - onFulField,
 - onRejected
 - })
 - }
 - }
 
axios.interceptors.response.use和axios.interceptors.request.use来定义请求和响应的拦截方法。
这说明axios上有响应拦截器和请求拦截器,那么如何在axios上实现呢:
- class Axios{
 - constructor(){
 - this.interceptors = {
 - request: new InterceptorsManage,
 - response: new InterceptorsManage
 - }
 - }
 - //....
 - }
 
在Axios的构造函数中新增interceptors属性,然后定义request和response属性用于处理请求和响应。
执行use方法时,会把传入的回调函数放到handlers数组中。
这时再回看使用方式,axios.interceptors.request.use方法是绑在axios实例上的,因此同样需要把Axios上的属性和方法转移到request上,将interceptors对象挂载到request方法上。
- function CreateAxiosFn() {
 - let axios = new Axios();
 - let req = axios.request.bind(axios);
 - utils.extend(req, Axios.prototype, axios)
 - //新增如下代码
 - utils.extend(req, axios)
 - return req;
 - }
 
但是现在request不仅要执行请求的发送,还要执行拦截器中handler的回调函数,因此还需要把request方法进行一下改造:
- request(config){
 - //拦截器和请求的队列
 - let chain = [this.sendAjax.bind(this),undefined];
 - //请求的拦截
 - this.interceptors.request.handlers.forEach(interceptor => {
 - chain.unshift(interceptor.onFulField,interceptor.onRejected);
 - })
 - //响应的拦截
 - this.interceptors.response.handlers.forEach(interceptor => {
 - chain.push(interceptor.onFulField,interceptor.onRejected)
 - })
 - let promise = Promise.resolve(config);
 - while(chain.length > 0){
 - //从头部开始依次执行请求的拦截、真正的请求、响应的拦截
 - promise = promise.then(chain.shift(),chain.shift());
 - }
 - return promise;
 - }
 - sendAjax(config){
 - return new Promise((resolve) => {
 - const {url='',method='get',data={}} = config;
 - const xhr = new XMLHttpRequest();
 - xhr.open(method,url,true);
 - xhr.onreadystatechange = () => {
 - if(xhr.readyState == 4 && xhr.status == 200){
 - resolve(xhr.responseText)
 - }
 - }
 - xhr.send(JSON.stringify(data));
 - })
 - }
 
最后执行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执行为:
- promise.then(
 - function (config) {
 - return config;
 - },
 - function (error) {
 - return Promise.reject(error);
 - }
 - )
 
请求时,执行为:
- promise.then(
 - this.sendAjax.bind(this),
 - undefined,
 - )
 
响应后,执行为:
- promise.then(
 - function (response) {
 - return response;
 - },
 - function (error) {
 - return Promise.reject(error);
 - }
 - )
 
这时我们测试一下拦截器的使用:
- function getMsg(){
 - axios.interceptors.request.use((config) => {
 - console.log('请求拦截:',config);
 - return config;
 - },err => {
 - return Promise.reject(err)
 - })
 - axios.interceptors.response.use(response => {
 - response = {
 - message: '响应数据替换',
 - data: response
 - }
 - return response
 - },err => {
 - console.log(err,'响应错误')
 - return Promise.reject(err)
 - })
 - axios.get({
 - url: 'http://localhost:3000/getTest',
 - }).then(res => {
 - console.log(res);
 - })
 - }
 
可以在控制台中看到拦截处理的打印输出,证明拦截成功!
5.总结
Axios天然支持Promise的性能让其方便对异步进行处理,同时又利用了Promise对请求进行了拦截,使得用户可以在请求过程中添加更多的功能,对请求的中断能自如操作。它的思想既清新朴实又不落入俗套,具有很好的借鉴意义。
看完这篇文章,你了解了Axios的核心原理了吗?
本文转载自微信公众号「Web前端严选」,可以通过以下二维码关注。转载本文请联系Web前端严选公众号。
                本文标题:手写Axios核心原理
                
                当前网址:http://www.csdahua.cn/qtweb/news19/366419.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网