axios原理

axios基本概念

axios是目前比较流行的一个js库,是一个基于promise的网络数据请求库,主要用于发送网络数据请求,从后台服务器上获取数据返回给前端。

优点特性:

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

axios多种方式调用

axios的常用几种方式就是:

  • axios.get(url,config); url表示请求地址,config表示配置对象
  • axios.post(url,config)
  • axios(config)

从上面可以看出axios既可以当函数axios()使用又可以当对象axios.get()使用,原理: 实质上axios是一个函数,但函数也属于是一个对象,所以同样可以向它身上追加属性和方法,我们所使用的axios是通过createInstance这个函数创造出来的,它简单实现的源码如下。
函数中实例化了Axios,Axios真正调用的是Axios原型链上的request方法;因此导出的axios需要关联到request方法,这里巧妙的通过bind函数进行关联,生成关联后的instance函数,同时指定它的调用上下文就是Axios的实例对象,因此instance调用时也能获取到实例对象上的defaults和interceptors属性;但是仅仅关联request还不够,再通过extend函数将Axios原型对象上的所有get、post等函数扩展到instance函数上,因此这也是我们才能够使用多种方式调用的原因所在。

function createInstance(defaultConfig) {
    // 1.实例化Axios
    var context = new Axios(defaultConfig);
    // 2.注意这里bind是一个自定义函数,返回一个函数()=>{Axios.prototype.request.apply(context,args)}
    // 这里request基本是Axios的核心方法,相当于将这些方法全部绑到了实例化的对象上
    var instance = bind(Axios.prototype.request, context);

    // Copy axios.prototype to instance
    // 3.将Axios原型链上的其他方法也都绑定到instance上去,这些方法的this会指向contxt
    utils.extend(instance, Axios.prototype, context);

    // Copy context to instance
    // 4.将contxt上的属性复制到instance上去
    utils.extend(instance, context);

    return instance;
}

axios的构造函数Axios,Axios函数在原型对象上还挂载了request、get、post等函数,但是get、post等函数最终都是通过request函数来发起请求的。而且request函数最终返回了一个Promise对象, 因此我们才能通过then函数接收到请求结果。

class Axios {
  constructor(instanceConfig) {
    this.defaults = instanceConfig;
    this.interceptors = {
      request: new InterceptorManager(),
      response: new InterceptorManager()
    };
  }
  request() {}
  
}

一个是将默认配置保存到defaults,另一个则是构造了interceptors拦截器对象
核心request函数主要作用:

  • 兼容多种传参方式(1. request(‘example/url’, { method: ‘post’ }); request({ url: ‘example/url’, method: ‘post’ }))
  • 合并参数
  • 通过promise的链式调用,处理请求、响应拦截器以及发送请求等操作。

工具函数

  • bind:将Axios原型上的方法挂载到instance上
  • extend:将构造函数 Axios.prototype 上的方法挂载到新的实例 instance 上,然后将原型各个方法中的 this 指向 context

axios的拦截器如何实现?用的设计模式是哪种?

拦截器实现就只有一个属性(用于保存拦截器)及三个原型方法(添加、移除、执行)。

实例化axios后,就可以调用use进行绑定拦截器,需要注意的是,在传递use方法的第一个参数时必须返回config,保证下一个promise能获取到处理后的参数。 options是可选参数对象,可传入两个属性(synchronous, runWhen),这么设计就是使用了责任链设计模式。

axios采用promise.resolve的方式将拦截器异步化。将所有请求拦截器放在请求方法之前unshift,所有的响应拦截器放在后push。遍历所有的方法通过promise的then方法将所有方法放在一条链上。

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
优点:

  • 你可以控制请求处理的顺序。
  • 单一职责原则。 你可对发起操作和执行操作的类进行解耦。
  • 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

责任链模式:执行的顺序是请求拦截器 -> 发起请求 -> 响应拦截器,这其实就是一个链条上串起了三个职责。

axios如何实现取消请求,和cancelToken如何使用

  1. 创建一个 CancelToken 的实例,它有一个 executor 函数,可以通过调用 executor 参数中的 cancel 函数来取消请求。(CancelToken内部通过promise实现,将promise的resolve方法暴露出去,手动控制promise状态)
  2. 在 axios 请求配置中指定 cancelToken 属性,将 CancelToken 实例传递进去。(axios 内部注册回调取消函数,真正的取消方法,XMLHttpRequest 的abort方法取消请求)
  3. 当我们需要取消请求时,调用 CancelToken 实例的 cancel() 方法即可取消对应的请求。(内部executor传出来的函数,函数中包含了resolvePromise(),手动改变promise状态,执行then(),执行所有被注册的函数,也就是执行真正的abort())

使用方法:

import axios from 'axios';
const CancelToken = axios.CancelToken;
let cancel;
// 【方法1】
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});
cancel('Operation canceled by the user.');
// 【方法2】
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})
source.cancel('Operation canceled by the user.');

源码:
axios/lib/adapters/xhr.js

// 当我们使用时,传入cancelToken配置

if (config.cancelToken || config.signal) {
  onCanceled = cancel => {
    if (!request) {
      return;
    }
    reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
    request.abort();
    request = null;
  };
  // 注册回调取消函数
  config.cancelToken && config.cancelToken.subscribe(onCanceled);
  if (config.signal) {
    config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
  }
}

lib/cancel/CancelToken.js

class CancelToken {
  constructor(executor) {
    if (typeof executor !== 'function') {
      throw new TypeError('executor must be a function.');
    }
    // 将promise中的resolve()暴露出去
    let resolvePromise;
    this.promise = new Promise(function promiseExecutor(resolve) {
      resolvePromise = resolve;
    });
    // 将 CancelToken 实例赋值给 token
    const token = this;
	// peomise状态改变后,执行oncanceled()
    this.promise.then(cancel => {
      if (!token._listeners) return;
      let i = token._listeners.length;
      while (i-- > 0) {
        token._listeners[i](cancel);
      }
      token._listeners = null;
    });
    // ??没看懂
    this.promise.then = onfulfilled => {
      let _resolve;
      const promise = new Promise(resolve => {
        token.subscribe(resolve);
        _resolve = resolve;
      }).then(onfulfilled);
      promise.cancel = function reject() {
        token.unsubscribe(_resolve);
      };
      return promise;
    };

    // 执行executor,这里定义的cancel函数即时业务中调用的cancel函数
    executor(function cancel(message, config, request) {
      if (token.reason) {
        return;
      }
      token.reason = new CanceledError(message, config, request);
      resolvePromise(token.reason); // 把接收到的“取消请求信息”token.reason传递给下一个then中成功函数作为参数
    });
  }
  subscribe(listener) {
    if (this.reason) {
      listener(this.reason);
      return;
    }
    if (this._listeners) {
      this._listeners.push(listener);
    } else {
      this._listeners = [listener];
    }
  }
  unsubscribe(listener) {
    if (!this._listeners) {
      return;
    }
    const index = this._listeners.indexOf(listener);
    if (index !== -1) {
      this._listeners.splice(index, 1);
    }
  }
  // cancelToken.source工厂函数,组合成source返回
  static source() {
    let cancel;
    const token = new CancelToken(function executor(c) {
      cancel = c; // 实现“在他处取消请求”,与上面“终止一个promise”异曲同工
    });
    return {
      token,
      cancel
    };
  }
}

手写一个简单axios

export function axios({ method, url, params, data }) {
  method = method.toUpperCase();
  return new Promise((resolve, reject) => {
    // 1.创建对象
    const xhr = new XMLHttpRequest();
    // 2.初始化
    let str = "";
    for (const k in params) {
      str += `${k}=${params[k]}&`;
    }
    str = str.slice(0, -1);
    xhr.open(method, url + "?" + str);
    // 3.发送请求
    if (method === "POST" || method === "PUT" || method === "DELETE") {
      // Content-type mime类型设置
      // 请求头
      xhr.setRequestHeader("Content-type", "application/json");
      // 请求体
      xhr.send(JSON.stringify(data));
    } else {
      // 如果是get方法
      xhr.send();
    }
    // 设置响应结果的类型为json
    xhr.responseType = "json";
    // 4.处理结果
    xhr.onreadystatechange = function () {
      //
      if (xhr.readyState === 4) {
        // 判断响应状态码
        if (xhr.status >= 200 && xhr.status <= 300) {
          // 成功的状态
          resolve({
            // 成功的状态码
            status: xhr.status,
            // 成功的字符串,响应字符串
            message: xhr.statusText,
            // 响应体
            body: xhr.response,
          });
        } else {
          reject(new Error("请求失败,失败的状态码为" + xhr.status));
        }
      }
    };
  });
}
axios.get = function (url, options) {
  // 发送AJAX请求 GET
  return axios(Object.assign(options, { method: "GET", url }));
};
axios.post = function (url, options) {
  // 发送AJAX请求 POST
  return axios(Object.assign(options, { method: "POST", url }));
};
axios.put = function (url, options) {
  // 发送AJAX请求 PUT
  return axios(Object.assign(options, { method: "PUT", url }));
};
axios.delete = function (url, options) {
  // 发送AJAX请求 DELETE
  return axios(Object.assign(options, { method: "DELETE", url }));
};
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Axios是一个基于Promise的HTTP客户端,用于在浏览器和Node.js中进行HTTP请求。它的底层原理涉及到几个主要的概念和步骤。 首先,Axios使用XMLHttpRequest对象(在浏览器中)或http模块(在Node.js中)来发送HTTP请求。它通过创建一个XMLHttpRequest实例或使用http模块的请求方法来进行通信。 其次,Axios允许你配置请求选项,例如请求的URL、请求方法、请求头、请求体等。你可以通过传递一个包含这些选项的配置对象来进行配置。 然后,Axios发送请求并等待服务器的响应。它使用Promise来处理异步操作,并返回一个Promise对象,该对象可以用于处理成功的响应或处理错误。 在发送请求之前,Axios还支持拦截器(interceptors)。拦截器可以在请求被发送之前或响应被处理之前对它们进行拦截和修改。你可以通过使用`axios.interceptors.request.use`方法来添加请求拦截器,使用`axios.interceptors.response.use`方法来添加响应拦截器。 当服务器响应到达时,Axios会解析响应数据并返回给调用者。它还会根据HTTP状态码将Promise解决为成功或失败。 最后,Axios提供了一些用于处理响应的方法,比如`.then`和`.catch`。你可以使用这些方法来处理成功的响应或处理错误。 总结来说,Axios的底层原理涉及到创建并配置HTTP请求、发送请求、拦截请求和响应、处理响应数据以及返回Promise对象供调用者处理。这使得Axios成为一个方便、强大且易于使用的HTTP客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值