zoukankan      html  css  js  c++  java
  • 初识Axios源码

    //暗号:axios

    处理异步在处理前后端的业务时的考虑问题,前端的Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)就是处理异步的问题,随后Fetch和Axios进一步封装了Ajax,使得接口更加丰富。

    1. Axios的创建

    axios({
      url,
      method,
      headers,
    })
    
    axios(url, {
      method,
      headers,
    })
    
    axios.get(url, {
      headers,
    })
    
    axios.post(url, data, {
      headers,
    })
    axios.create(optios)

    axios创建方法方法: 1. axios() -- axios方法创建, 2. axios.create()对象的create方法创建, 3.axios.request()

    // Axios引入 (axios/index.js)
    module.exports = require('./lib/axios')

    axios/lib/axios.js中的createInstance能够实现axios的多种创建方式

    function createInstance(defaultConfig) {

     // 创建一个Axios的实例但是最终返回的并不是这个实例
    var context = new Axios(defaultConfig);

     // 创建一个instance的容器,指向Axios.prototyep.request方法,并上下文指向context,最终返回。 
     // 我们能用Axios() => instance()
    var instance = bind(Axios.prototype.request, context);  // 把Axios.prototype上的方法扩展到instance对象上。
     // 这样instance又有了get、post、put等方法
     // 并指向上下文context,这样执行Axios原型链上的方法时,this会指向context utils.extend(instance, Axios.prototype, context); // 把context对象上的自身属性和方法扩展到instance上
     // extend方法会遍历对象本身,不会表里原型链上的方法和属性
    // instance就有了create,default, interceptors的方法和属性
    utils.extend(instance, context); return instance; } // 调用axios()方法时候,使用默认配置 var axios = createInstance(defaults); // Expose Axios class to allow class inheritance axios.Axios = Axios; // Factory for creating new instances
    // 合并配置项
    axios.create = function create(instanceConfig) { return createInstance(mergeConfig(axios.defaults, instanceConfig)); };
    Axios模块是axios的核心,一个Axios实例对应一个axios对象,其他方法都是对Axios内容的扩展
    Axios构造函数的核心方法是request方法,各种axios的调用方式最终都是通过request方法发请求的。
    调用axios等同于Axios.prototype.request
    调用axios.reuqest等同于Axios.prototype.request
    调用axios.get等同于Axios.ptototype.get
    // /lib/core/Axios.js

    function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; } Axios.prototype.request = function request(config) { // ...处理配置和参数合并处理,省略代码 }; // 为支持的请求方法提供别名 utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { Axios.prototype[method] = function(url, config) { return this.request(utils.merge(config || {}, { method: method, url: url })); }; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { Axios.prototype[method] = function(url, data, config) { return this.request(utils.merge(config || {}, { method: method, url: url, data: data })); }; });

    2. 统一发送请求的适配器adapter

    adapter是xhr.js封装的promise对象, 它统一了不同环境发送请求的需求。 
    // default.js
    // 适配器
    var
    adapter = config.adapter || defaults.adapter; return adapter(config).then(function onAdapterResolution(response) { throwIfCancellationRequested(config); // 处理不同环境发出的request请求 function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== 'undefined') { // 处理浏览器的发出的请求 adapter = require('./adapters/xhr');
    }
    else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { // For node use HTTP adapter // 处理node环境的发出的请求 adapter = require('./adapters/http'); } return adapter; }
    // xhr.js
    
    // 返回xhr的promise对象
    
    module.exports = function xhrAdapter(config) {
      return new Promise(function dispatchXhrRequest(resolve, reject) {
     
        var request = new XMLHttpRequest();
    
        // Send the request
        request.send(requestData);
      });
    };

    3. InterceptoManager 拦截器

    Axios.js中有一个拦截器InterceptorManager, 可以简单理解为一个request或者response的数组,每次从队列头部或者尾部传入request或者response的promise对象fullfilled和rejected函数
    // InterceptorManager.js
    // InterceptorManager方法维护一个数组
    function InterceptorManager() {
      this.handlers = [];
    }
    
    // 每次在数组尾部加入promise对象, use方法进行注册
    InterceptorManager.prototype.use = function use(fulfilled, rejected) {
      this.handlers.push({
        fulfilled: fulfilled,
        rejected: rejected
      });
      return this.handlers.length - 1;
    };

    4. 执行链处理拦截器的请求和响应

    处理ajax的promise请求前,执行链chain会把拦截器数组中的promise对象请求unshift和响应push加入chain数组中, 

      // 创建一个执行链 [dispatchRequest, undefined], dispatchRequest是适配器ajax中封装的promise请求
      var chain = [dispatchRequest, undefined];
      var promise = Promise.resolve(config);
    
      this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
        chain.unshift(interceptor.fulfilled, interceptor.rejected);
      });
    
      this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
        chain.push(interceptor.fulfilled, interceptor.rejected);
      });
    
     // 执行顺序promise.then(rs2, rf2).then(rs1, rf1). then(dispatchRequest, undefine).then(ps1, pf1).then(ps2, pf2)...
    while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; };

    5.  执行链处理请求和响应的promise对象的顺序

        数组的 shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
        每次执行while循环,从chain数组里按序取出两项,并分别作为promise.then方法的第一个和第二个参数
    
        按照我们使用InterceptorManager.prototype.use添加拦截器的规则,正好每次添加的就是我们通过InterceptorManager.prototype.use方法添加的成功和失败回调
    
        通过InterceptorManager.prototype.use往拦截器数组里添加拦截器时使用的数组的push方法,
        对于请求拦截器,从拦截器数组按序读到后是通过unshift方法往chain数组数里添加的,又通过shift方法从chain数组里取出的,所以得出结论:对于请求拦截器,先添加的拦截器会后执行
        对于响应拦截器,从拦截器数组按序读到后是通过push方法往chain数组里添加的,又通过shift方法从chain数组里取出的,所以得出结论:对于响应拦截器,添加的拦截器先执行
    
        第一个请求拦截器的fulfilled函数会接收到promise对象初始化时传入的config对象,而请求拦截器又规定用户写的fulfilled函数必须返回一个config对象,所以通过promise实现链式调用时,每个请求拦截器的fulfilled函数都会接收到一个config对象
    
        第一个响应拦截器的fulfilled函数会接受到dispatchRequest(也就是我们的请求方法)请求到的数据(也就是response对象),而响应拦截器又规定用户写的fulfilled函数必须返回一个response对象,所以通过promise实现链式调用时,每个响应拦截器的fulfilled函数都会接收到一个response对象
    
        任何一个拦截器的抛出的错误,都会被下一个拦截器的rejected函数收到,所以dispatchRequest抛出的错误才会被响应拦截器接收到。
    
        因为axios是通过promise实现的链式调用,所以我们可以在拦截器里进行异步操作,而拦截器的执行顺序还是会按照我们上面说的顺序执行,也就是 dispatchRequest 方法一定会等待所有的请求拦截器执行完后再开始执行,响应拦截器一定会等待 dispatchRequest 执行完后再开始执行。
    

     // 暗号:axios

  • 相关阅读:
    Python 函数式编程学习
    Perl 学习笔记-目标操作
    Ubuntu14.04-LTS 从系统安装到配置可用
    Perl 学习笔记-文件测试
    Perl 学习笔记-模块
    插曲 强大的神器 vmware
    18 11 16 网络通信 ---- 多线程 同步概念 解决资源互斥的问题
    18 11 15 网络通信 ---- 多任务----线程 threading
    18 11 14 案例 下载文件后端编写
    18 11 13 装了ssd 继续 网络通信 tcp 客户端的创建
  • 原文地址:https://www.cnblogs.com/Sam-r/p/14059521.html
Copyright © 2011-2022 走看看