编程 axios 源码解析:十分钟带你实现一个 mini-axios

2024-11-18 22:27:47 +0800 CST views 517

axios 源码解析:十分钟带你实现一个 mini-axios

本文将带你在十分钟内快速了解并实现一个精简版的 axios,整个流程分为以下 5 个大部分:
images

  1. 准备测试环境
  2. axios 核心请求构建
  3. 多宿主环境(浏览器 || node)适配思想
  4. 拦截器的实现原理
  5. 如何取消请求

1. 准备基础的测试环境

1.1 基于 Koa 准备一个最简单的服务程序:

我们使用 Koa 搭建一个基础的服务端环境,用于测试浏览器端的请求。

import Koa from 'koa';
const app = new Koa();

// 一个简单的路由处理函数
app.use(async ctx => {
  ctx.body = 'Hello, World!';
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

为支持跨域请求,还需要添加支持跨域的中间件:

app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', '*');  // 允许所有来源
  ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  ctx.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  if (ctx.request.method === 'OPTIONS') {
    ctx.status = 200;
    return;
  }
  
  await next();
});

1.2 准备浏览器和 Node.js 端测试环境:

在浏览器端测试环境中,我们可以准备一个简单的 HTML 文件:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./index.js"></script>
</body>
</html>

基础的 Node.js 测试环境相对简单,只需确保代码不包含浏览器相关的 API,Node.js 也可以运行。

2. axios 核心请求构建

2.1 axios 入口模块

首先,我们创建一个 axios.js 文件,开发 Axios 类,并初始化 axios 工厂函数,最后导出 axios 实例。

// util.js
export const mergeConfig = (config1, config2) => {
  return Object.assign(config1, config2);
};

// axios.js
import { mergeConfig } from './utils.js';

class Axios {
  constructor(defaultConfig) {
    this.defaultConfig = defaultConfig;
  }

  requiest(url, options) {
    try {
      this._requiest(url, options);
    } catch (error) {
      console.error(error);
    }
  }

  _requiest(url, options) {
    console.log('开始发送请求', url, options);
  }
}

function createInstance(defaultConfig) {
  const context = new Axios(defaultConfig);
  const instance = Axios.prototype.requiest.bind(context);
  instance.create = function (instanceConfig) {
    return createInstance(mergeConfig(defaultConfig, instanceConfig));
  };
  return instance;
}

const axios = createInstance({
  timeout: 0,
  adapter: ["xhr", "http", "fetch"],
  beseURL: "",
  headers: {}
});

axios.Axios = Axios;
axios.default = axios;

export default axios;

现在 axios 的基础功能已经实现,可以在测试文件中导入并进行测试。

import axios from './axios.js';
axios('http://localhost:3000/');

2.2 参数归一化

参数归一化是一种常见的技术,统一函数的参数格式以简化逻辑。

requestHelper(url, options) {
  let config = {};
  if (typeof url === 'string') {
    config = options || {};
    config.url = url;
  } else if (typeof url === 'object') {
    config = url;
  }
  return config;
}

_requiest(url, options) {
  const config = mergeConfig(this.defaultConfig, this.requestHelper(url, options));
  console.log('最终的配置', config);
}

3. 多环境请求发送

axios 需要兼容不同环境,因此提出了适配器的概念,根据当前环境选择不同的请求 API,如 xhrfetch 或 Node.js 的 http 模块。

3.1 实现适配器核心逻辑

我们首先创建一个 Adapte.js 文件,负责适配不同的请求模块:

export default {
  getAdapter(adapters) {
    adapters = Array.isArray(adapters) ? adapters : [adapters];
    let handler = null;
    for (const adapter of adapters) {
      handler = adapteConfig[adapter];
      if (handler) break;
    }
    return handler;
  }
};

然后,在 dispatchRequest 模块中统一处理请求:

export default function dispatchRequest(config) {
  console.log('开始请求发送', config);
}

axios.js 中调用该模块:

import dispatchRequest from './dispatchRequest.js';
_requiest(url, options) {
  const config = mergeConfig(this.defaultConfig, this.requestHelper(url, options));
  dispatchRequest(config);
}

4. axios 拦截器实现

拦截器允许开发者在请求或响应前后进行操作。axios 实现拦截器的方式是通过一个队列,依次执行任务。

4.1 实现拦截器逻辑

我们定义一个 InterceptorManager 类,用于管理拦截器:

class InterceptorManager {
  constructor() {
    this.handlers = [];
  }

  use(fulfilled, rejected) {
    this.handlers.push({ fulfilled, rejected });
    return this.handlers.length - 1;
  }
}

export default InterceptorManager;

Axios 类中导入并初始化拦截器:

import InterceptorManager from './InterceptorManager.js';

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

在请求时处理拦截器:

_requiest(url, options) {
  const config = mergeConfig(this.defaultConfig, this.requestHelper(url, options));
  const chain = [dispatchRequest, undefined];
  this.interceptors.request.handlers.forEach(interceptor => {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  this.interceptors.response.handlers.forEach(interceptor => {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  let promise = Promise.resolve(config);
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
}

5. axios 取消请求的实现

5.1 CancelToken 实现

CancelToken 通过 Promise 来管理取消请求事件。

class CancelToken {
  constructor(executor) {
    let resolvePromise;
    this.promise = new Promise(resolve => {
      resolvePromise = resolve;
    });
    executor(message => {
      this.reason = new CanceledError(message);
      resolvePromise(this.reason);
    });
  }

  static source() {
    let cancel;
    const token = new CancelToken(c => {
      cancel = c;
    });
    return { token, cancel };
  }
}

总结

本文简要介绍了如何使用 5 个步骤实现一个 mini-axios,涵盖了基础请求构建、多环境适配、拦截器实现以及取消请求功能。通过这个实现,我们对 axios 的内部原理有了更深入的理解,能够帮助我们在项目中更加灵活地使用它。

复制全文 生成海报 JavaScript 网络请求 前端开发

推荐文章

Redis和Memcached有什么区别?
2024-11-18 17:57:13 +0800 CST
html一个包含iPhoneX和MacBook模拟器
2024-11-19 08:03:47 +0800 CST
ElasticSearch简介与安装指南
2024-11-19 02:17:38 +0800 CST
15 个你应该了解的有用 CSS 属性
2024-11-18 15:24:50 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
支付页面html收银台
2025-03-06 14:59:20 +0800 CST
企业官网案例-芊诺网络科技官网
2024-11-18 11:30:20 +0800 CST
Manticore Search:高性能的搜索引擎
2024-11-19 03:43:32 +0800 CST
Go 中的单例模式
2024-11-17 21:23:29 +0800 CST
20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
如何开发易支付插件功能
2024-11-19 08:36:25 +0800 CST
免费常用API接口分享
2024-11-19 09:25:07 +0800 CST
Go 1.23 中的新包:unique
2024-11-18 12:32:57 +0800 CST
Linux 常用进程命令介绍
2024-11-19 05:06:44 +0800 CST
2025年,小程序开发到底多少钱?
2025-01-20 10:59:05 +0800 CST
php 连接mssql数据库
2024-11-17 05:01:41 +0800 CST
动态渐变背景
2024-11-19 01:49:50 +0800 CST
PHP如何进行MySQL数据备份?
2024-11-18 20:40:25 +0800 CST
Vue3中的v-bind指令有什么新特性?
2024-11-18 14:58:47 +0800 CST
阿里云免sdk发送短信代码
2025-01-01 12:22:14 +0800 CST
Nginx负载均衡详解
2024-11-17 07:43:48 +0800 CST
js一键生成随机颜色:randomColor
2024-11-18 10:13:44 +0800 CST
程序员茄子在线接单