• 【axios三部曲】三、极简核心造轮子


    axios执行流程

    目录结构

    image.png

    如果要一个极简核心的话,我们只需要 request 请求函数,和一个 XMLHttpRequest 即可,拦截器,适配器 dispatchRquest 都可以不要

    Axios构造函数

    我们从Axios构造函数开始写
    core/Axios.js

    import utils from "../utils";
    import mergeConfig from "./mergeConfig";
    import xhr from "./xhr";
    
    // 核心文件
    
    function Axios(config) {
      this.defaults = config;
    }
    
    Axios.prototype.request = function (config) {
      // 处理axios(url[,config])
      if (typeof config === "string") {
        config = arguments[1] || {};
        config.url = arguments[0];
      } else {
        config = config || {};
      }
    
      // 合并config
      config = mergeConfig(this.defaults, config);
    
      console.log(config);
    
      // 设置默认method
      if (config.method) {
        config.method = config.method.toLowerCase();
      } else if (this.defaults.method) {
        config.method = this.defaults.method.toLowerCase();
      } else {
        config.method = "get";
      }
    
      let promise = Promise.resolve(config);
      promise = promise.then(xhr);
    
      return promise;
    };
    
    utils.forEach(["delete", "get", "head", "options"], function (method) {
      Axios.prototype[method] = function (url, config) {
        return this.request(
          utils.merge(config || {}, {
            method: method,
            url: url,
          })
        );
      };
    });
    
    utils.forEach(["post", "put", "patch"], function (method) {
      Axios.prototype[method] = function (url, data, config) {
        return this.request(
          utils.merge(config || {}, {
            method: method,
            url: url,
            data: data,
          })
        );
      };
    });
    
    export default Axios;
    
    

    只有一个最简单request方法

    mergeConfig

    core/mergeConfig.js
    也没有做过多的判断,只是优先把config2的值拷贝到config上,其次再使用config1的值

    import utils from "../utils";
    
    // 合并两个配置对象,返回一个新的对象
    function mergeConfig(config1, config2) {
      let config = {};
    
      let axiosKeys = Array.from(
        new Set(Object.keys(config1).concat(Object.keys(config2)))
      );
    
      // 优先使用config2身上的
      utils.forEach(axiosKeys, function (prop) {
        if (utils.isObject(config2[prop])) {
          config[prop] = utils.merge(config1[prop], config2[prop]);
        } else if (typeof config2[prop] !== "undefined") {
          config[prop] = config2[prop];
        } else if (utils.isObject(config1[prop])) {
          config[prop] = utils.merge(config1[prop]);
        } else if (typeof config1[prop] !== "undefined") {
          config[prop] = config1[prop];
        }
      });
    
      return config;
    }
    
    export default mergeConfig;
    
    

    xhr

    core/xhr.js
    一个简单的XMLHttpRequest

    // XMLHttpRequest
    
    // 返回一个promise
    const xhr = function (config) {
      return new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest();
    
        xhr.open(config.method, config.url);
        xhr.send(config.data);
    
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              // 返回response数据,自定义格式
              resolve({
                config: config,
                data: JSON.parse(xhr.responseText),
                request: xhr,
                headers: xhr.getAllResponseHeaders(),
                status: xhr.status,
                statusText: xhr.statusText,
              });
            } else {
              reject("数据请求错误:" + xhr.statusText);
            }
          }
        };
      });
    };
    
    export default xhr;
    
    

    utils

    utils.js
    一些工具函数

    function forEach(obj, fn) {
      if (obj === null || typeof obj === "undefined") {
        return;
      }
    
      if (typeof obj !== "object") {
        obj = [obj];
      }
    
      if (Array.isArray(obj)) {
        for (let i = 0; i < obj.length; i++) {
          fn.call(null, obj[i], i, obj);
        }
      } else {
        for (let key in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, key)) {
            fn.call(null, obj[key], key, obj);
          }
        }
      }
    }
    
    function bind(fn, thisArg) {
      // 这里用fn.apply来实现函数this绑定,注意需要返回的也是一个函数,所以这里包一层
      return function wrap() {
        // 把多个参数,转成数组的形式
        let args = new Array(arguments.length);
        for (let i = 0; i < args.length; i++) {
          args[i] = arguments[i];
        }
    
        // args是一个数组,apply调用方式
        return fn.apply(thisArg, args);
      };
    }
    
    // 实现继承,b->a 如果是函数要绑定this指向
    function extend(a, b, thisArg) {
      forEach(b, function (val, key) {
        if (thisArg && typeof val === "function") {
          a[key] = bind(val, thisArg);
        } else {
          a[key] = val;
        }
      });
      return a;
    }
    
    // 合并对象,返回一个新的对象
    function merge() {
      let result = {};
    
      function assignValue(val, key) {
        // result里的是一个对象,并且val也是一个对象,深拷贝
        if (typeof result[key] === "object" && typeof val === "object") {
          result[key] = merge(result[key], val);
        } else if (typeof result[key] === "object") {
          result[key] = merge({}, val);
        } else {
          result[key] = val;
        }
      }
    
      // 有多个参数要合并,依次循环遍历,合并到result
      for (let i = 0; i < arguments.length; i++) {
        forEach(arguments[i], assignValue);
      }
    
      return result;
    }
    
    function isObject(val) {
      return val !== null && typeof val === "object";
    }
    
    export default {
      forEach,
      bind,
      extend,
      merge,
      isObject,
    };
    
    

    defaults

    defaults.js
    默认的头配置文件

    let defaults = {
      method: "get",
    };
    
    export default defaults;
    
    

    axios入口文件

    // 入口文件
    import Axios from "./core/Axios";
    import utils from "./utils";
    import mergeConfig from "./core/mergeConfig";
    import defaults from "./defaults";
    
    function createInstance(defaultConfig) {
      let context = new Axios(defaultConfig);
      let instance = utils.bind(Axios.prototype.request, context);
    
      // extend prototype
      utils.extend(instance, Axios.prototype, context);
      // extend Axios属性
      utils.extend(instance, context);
    
      instance.create = function (newConfig) {
        return createInstance(mergeConfig(defaultConfig, newConfig));
      };
    
      return instance;
    }
    
    let axios = createInstance(defaults);
    
    console.dir(axios);
    
    // console.log(axios);
    
    // axios({
    //   url: "http://localhost:3000/posts",
    //   method: "get",
    // }).then((response) => {
    //   console.log(response);
    // });
    
    axios
      .get("http://localhost:3000/posts", {
        headers: {
          common: 111,
        },
      })
      .then((response) => {
        console.log(response);
      });
    
    

    总结

    • promise的使用
    • utils里面的工具函数比较实用,要注意的是forEach,bind,extend,merge这几个函数
    • 构造函数 + prototype的方式来组织代码,现在已经有更好的选择 class
    • axios库的核心就是axios是一个函数(request),它身上也挂有方法(get,post),我们可以学习这种编程方法
    • axios的config参数合并也是重点,有优先级,默认的最低,最后方法传入的最高

    源码地址

  • 相关阅读:
    这是一个关于Latex的测试
    在Mac下配置php开发环境:Apache+php+MySql
    CSS 颜色代码大全
    APP中关于Android和IOS与网页交互
    linux crontab
    dedecms中的内容页中的变量
    lamp中的Oracle数据库链接
    phpcms使用session的方法
    linux系统下nginx安装目录和nginx.conf配置文件目录
    window7+wamp环境配置Oracle数据库连接
  • 原文地址:https://www.cnblogs.com/lostyu/p/axios3.html
Copyright © 2020-2023  润新知