面试官:你懂Axios吗?我:给你手撸一个

Axios 是一个非常流行的前端 HTTP 请求库,它可以方便地发起 HTTP 请求并处理响应结果。Axios 不仅支持基本的 GET、POST 等请求方式,还提供了丰富的配置项和拦截器机制,使得开发者可以对请求和响应进行全方位的控制和定制。

在本文中,我们将通过手写 Axios 的方式来学习请求拦截器和响应拦截器的实现。具体来说,我们将会实现一个基于 XMLHttpRequest 对象的 Axios 类,并在其中添加请求拦截器和响应拦截器。

实现 Axios 类

首先,我们需要创建一个 Axios 类,并在其中定义一些属性和方法。具体来说,我们需要:

  • 定义一个拦截器对象,用于存储请求拦截器和响应拦截器;

  • 定义一个 request 方法,用于发送 HTTP 请求;

  • 定义一个 get 方法,用于发送 GET 请求;

  • 定义一个 post 方法,用于发送 POST 请求。

下面是基本的 Axios 类的实现代码:

class Axios {
  constructor() {
    // 定义拦截器对象
    this.interceptors = {
      request: new InterceptorManager(),
      response: new InterceptorManager(),
    };
  }

  // 发送请求的方法
  request(config) {
    // 创建一个 Promise 对象
    const promise = new Promise((resolve, reject) => {
      // 创建一个 XMLHttpRequest 对象
      const xhr = new XMLHttpRequest();

      // 处理响应结果的函数
      const handleResponse = () => {
        if (xhr.readyState !== 4) {
          return;
        }

        if (xhr.status === 200) {
          // 成功响应,执行响应拦截器后解析响应数据并返回
          let response = xhr.response;
          response = this.interceptors.response.run(response);
          resolve(response);
        } else {
          // 响应失败,直接 reject
          reject(xhr.statusText);
        }
      };

      // 设置请求方式和 URL
      xhr.open(config.method, config.url);

      // 设置请求头
      if (config.headers) {
        Object.keys(config.headers).forEach((key) => {
          xhr.setRequestHeader(key, config.headers[key]);
        });
      }

      // 发送请求
      xhr.send(config.data);

      // 监听状态变化
      xhr.onreadystatechange = handleResponse;
    });

    return promise;
  }

  // 发送 GET 请求的方法
  get(url, config) {
    config = Object.assign({}, config, { method: 'GET', url });
    return this.request(config);
  }

  // 发送 POST 请求的方法
  post(url, data, config) {
    config = Object.assign({}, config, { method: 'POST', url, data });
    return this.request(config);
  }
}

在上述代码中,我们定义了一个 Axios 类,其中:

  • 构造函数中定义了一个拦截器对象,包括请求拦截器和响应拦截器;

  • request 方法用于发送 HTTP 请求,它接收一个配置对象作为参数,并返回一个 Promise 对象;

  • get 方法用于发送 GET请求,它接收一个 URL 和配置对象作为参数,并返回一个 Promise 对象;

  • post 方法用于发送 POST 请求,它接收一个 URL、请求体数据和配置对象作为参数,并返回一个 Promise 对象。

现在我们已经定义了 Axios 类的基本骨架,接下来我们需要实现请求拦截器和响应拦截器的功能。

实现请求拦截器和响应拦截器

请求拦截器和响应拦截器是 Axios 提供的两个非常重要的功能,它们允许我们在请求和响应过程中执行一些自定义的逻辑,例如:

  • 统一设置请求头;

  • 统一处理请求错误;

  • 统一处理响应错误;

  • 统一处理响应数据。

在 Axios 中,我们可以通过拦截器对象来实现请求拦截器和响应拦截器。拦截器对象包含两个属性:request 和 response。其中,request 属性包含了请求拦截器,response 属性包含了响应拦截器。

每个拦截器对象都包含了一个 run 方法,该方法接收一个参数,并将参数传递给所有注册在该拦截器对象上的拦截器函数。

现在我们来实现拦截器对象和拦截器函数。

class InterceptorManager {
  constructor() {
    // 定义拦截器数组
    this.interceptors = [];
  }

  // 注册拦截器函数
  use(fulfilled, rejected) {
    this.interceptors.push({ fulfilled, rejected });
    return this.interceptors.length - 1;
  }

  // 执行拦截器函数
  run(data) {
    let result = data;
    this.interceptors.forEach((interceptor) => {
      const { fulfilled, rejected } = interceptor;
      if (fulfilled) {
        result = fulfilled(result);
      }
    });
    return result;
  }
}

在上述代码中,我们定义了一个 InterceptorManager 类,其中:

  • 构造函数中定义了一个拦截器数组;

  • use 方法用于注册拦截器函数,它接收两个参数:fulfilled 和 rejected,分别表示成功拦截器和失败拦截器。它返回一个拦截器函数的索引,可以用于后续的拦截器注销;

  • run 方法用于执行拦截器函数,它接收一个参数,并将参数传递给所有注册在该拦截器对象上的拦截器函数,最终返回拦截器函数的结果。

现在我们已经实现了拦截器对象和拦截器函数,接下来我们将它们与 Axios 类结合起来,实现请求拦截器和响应拦截器的功能。

class Axios {
  constructor() {
    // 定义拦截器对象
    this.interceptors = {
      request: new InterceptorManager(),
      response: new InterceptorManager(),
    };
  }

    // 发送 GET 请求
    get(url, config) {
       return this._requestMethodWithoutData("get", url, config);
    }
    
    // 发送 POST 请求
    post(url, data, config) {
       return this._requestMethodWithData("post", url, data, config);
    }
    
    // 发送 PUT 请求
    put(url, data, config) {
       return this._requestMethodWithData("put", url, data, config);
    }
    
    // 发送 PATCH 请求
    patch(url, data, config) {
       return this._requestMethodWithData("patch", url, data, config);
    }
    
    // 发送 DELETE 请求
    delete(url, config) {
       return this._requestMethodWithoutData("delete", url, config);
    }
 }
    
// 创建 axios 实例
const axios = new Axios();
    
// 导出 axios 实例
export default axios;

在上述代码中,我们对 Axios 类进行了修改,主要增加了以下功能:

  • 在构造函数中定义了 request 和 response 拦截器对象;

  • 定义了 interceptor 和 eject 方法,用于注册和注销拦截器;

  • 在 _request 方法中使用拦截器对象的 run 方法,分别执行 request 和 response 拦截器中的拦截器函数;

  • 重写了 GET、POST、PUT、PATCH 和 DELETE 方法,使用 _requestMethodWithData 和 _requestMethodWithoutData 方法来统一处理请求参数;

  • 创建了一个 axios 实例,并导出。

现在我们已经完成了 axios 的基本实现,并且增加了请求拦截器和响应拦截器的功能。

使用手写的 axios

我们可以使用手写的 axios 发送 HTTP 请求,使用方式与原生的 axios 差不多,例如:

import axios from "./axios";
// 注册请求拦截器
axios.interceptors.request.use(
  (config) => {
    // 统一设置请求头
    config.headers["X-Requested-With"] = "XMLHttpRequest";
    return config;
  },
  (error) => {
    // 统一处理请求错误
    console.error("请求错误:", error);
    return Promise.reject(error);
  }
);
// 注册响应拦截器
axios.interceptors.response.use(
  (response) => {
    // 统一处理响应数据
    if (response.status === 200) {
      return response.data;
    } else {
      return Promise.reject(response);
    }
  },
  (error) => {
    // 统一处理响应错误
    console.error("响应错误:", error);
    return Promise.reject(error);
  }
);
// 发送 GET 请求
axios.get("https://jsonplaceholder.typicode.com/todos/1").then((data) => {
  console.log(data);
});
// 发送 POST 请求
axios.post("https://jsonplaceholder.typicode.com/posts", {
  title: "foo",
  body: "bar",
  userId: 1,
}).then((data) => {
  console.log(data);
});

在上述代码中,我们通过 axios.interceptors.request.use 和 axios.interceptors.response.use 方法注册了请求拦截器和响应拦截器。我们可以在拦截器中统一设置请求头、统一处理请求错误、统一处理响应数据、统一处理响应错误等操作。

然后我们通过 axios.get 和 axios.post 方法发送了 GET 和 POST 请求,并在 then 方法中获取响应数据,并进行了打印输出。这里的使用方式与原生的 axios 差不多,但是我们可以使用我们手写的 axios,并在其中添加我们需要的功能,例如请求拦截器和响应拦截器。

总结

本文通过手写一个简化版的 axios 实现了请求拦截器和响应拦截器的功能,并且介绍了 axios 的基本用法。在实现过程中,我们首先定义了一个 Axios 类,并在其中实现了基本的 HTTP 请求方法。然后,我们增加了请求拦截器和响应拦截器的功能,并在其中实现了拦截器对象的注册、注销和执行方法。

手写一个 axios 的过程虽然比较繁琐,但是它可以帮助我们更深入地理解 axios 的实现原理,以及如何使用 axios 发送 HTTP 请求。同时,我们还可以在其中添加我们需要的功能,例如请求拦截器和响应拦截器,以便于我们统一处理请求和响应数据,提高开发效率。

猜你喜欢

转载自blog.csdn.net/qq_38261819/article/details/129409214