axios三部曲三极简核心造轮子

Posted Tony

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了axios三部曲三极简核心造轮子相关的知识,希望对你有一定的参考价值。

axios执行流程

目录结构

如果要一个极简核心的话,我们只需要 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参数合并也是重点,有优先级,默认的最低,最后方法传入的最高

源码地址

以上是关于axios三部曲三极简核心造轮子的主要内容,如果未能解决你的问题,请参考以下文章

别再重复造轮子了,推荐使用 Google Guava 开源工具类库,真心强大!

别再重复造轮子了,推荐使用 Google Guava 开源工具类库,真心强大!

脉脉热帖:为啥大厂都热衷于造轮子?

为啥大中型公司都热衷于造轮子?

造轮子和用轮子:快速入门JavaScript模块化

「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子?