axios

104 阅读6分钟

一、Axios 的核心原理

从本质上讲,Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。这句话包含了它的几个核心要点,我们来逐一拆解。

浏览器端:axios是利用的promise+ xhr请求.

1. 基于 Promise 的 API

这是 Axios 最基础也是最重要的特性。在早期,网络请求(例如使用 XMLHttpRequest)是基于回调函数的。这很容易导致“回调地狱”(Callback Hell),代码难以阅读和维护。

codeJavaScript

// 回调地狱的伪代码
ajax('api/user', function(user) {
    ajax('api/posts?userId=' + user.id, function(posts) {
        ajax('api/comments?postId=' + posts[0].id, function(comments) {
            console.log(comments);
        }, handleError);
    }, handleError);
}, handleError);

Axios 将这些异步操作封装成了 Promise。Promise 是一种更优雅地处理异步操作的模式,它允许我们使用 .then(), .catch(), .finally() 以及 async/await 语法来编写更线性和可读的代码。

codeJavaScript

// 使用 Axios (async/await)
async function fetchComments() {
    try {
        const userResponse = await axios.get('api/user');
        const postsResponse = await axios.get('api/posts?userId=' + userResponse.data.id);
        const commentsResponse = await axios.get('api/comments?postId=' + postsResponse.data[0].id);
        console.log(commentsResponse.data);
    } catch (error) {
        handleError(error);
    }
}

原理: Axios 内部创建并返回一个 Promise 对象。当请求成功时,它会调用 Promise 的 resolve 函数,并将响应数据传递出去;当请求失败时,它会调用 reject 函数,并传递一个错误对象

2. 同构性(Isomorphic):浏览器与 Node.js 通用

这是一个非常强大的特性。 “同构”意味着同一套代码可以在不同的环境(客户端和服务器端)中运行。  Axios 是如何做到的呢?

  • 在浏览器端:它底层封装的是 XMLHttpRequest (XHR) 对象。这是浏览器提供的原生 API,用于发送 HTTP 请求。
  • 在 Node.js 端:它底层封装的是 Node.js 内置的 http 或 https 模块。因为 Node.js 环境中没有 XHR 这个浏览器 API。

Axios 通过一个**适配器(Adapter)**层来判断当前运行环境,并选择合适的底层 API 来发送请求。这使得开发者无需关心底层实现细节,用一套统一的 axios() API 即可完成在任何环境下的网络请求。

3. 拦截器(Interceptors)

拦截器是 Axios 的一个核心且非常实用的功能。它允许你在请求发送之前响应返回之后对它们进行拦截和处理。

  • 请求拦截器 (Request Interceptor) :在请求被发送到服务器之前,可以用来做一些统一处理,例如:

    • 为每个请求添加认证 token 到请求头(headers)中。
    • 开启请求加载动画(loading aniamtion)。
    • 对请求数据进行统一的格式化。
  • 响应拦截器 (Response Interceptor) :在 .then() 或 .catch() 被触发之前,可以对响应数据进行预处理,例如:

    • 统一处理 HTTP 错误码(如 401 未授权,直接跳转到登录页)。
    • 关闭加载动画。
    • 对返回的数据进行解构,只返回核心的 data 部分。

原理: Axios 内部维护了两个拦截器数组(一个请求,一个响应)。当一个请求发出时,它会像一个链条一样执行:请求拦截器 -> 发送请求 -> 响应拦截器 -> 返回给用户的 Promise。请求拦截器是“后进先出”(LIFO)的顺序执行,而响应拦截器是“先进先出”(FIFO)的顺序执行。

codeJavaScript

// 添加请求拦截器
axios.interceptors.request.use(config => {
  // 在发送请求之前做些什么
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(response => {
  // 对响应数据做点什么,例如只返回 data
  return response.data;
}, error => {
  // 对响应错误做点什么
  if (error.response.status === 401) {
    // 跳转到登录页
  }
  return Promise.reject(error);
});
4. 请求和响应数据转换

Axios 会自动处理请求和响应数据的转换。

  • 请求时: 如果你传递一个 JavaScript 对象作为 data,Axios 会自动将其 JSON.stringify() 并设置请求头 Content-Type 为 application/json。
  • 响应时: 如果收到的响应头 Content-Type 是 application/json,Axios 会自动为你 JSON.parse() 响应体,所以你直接就能拿到 JavaScript 对象。

原理: 这是通过 transformRequest 和 transformResponse 这两个配置项实现的。它们是一组函数,允许你在请求发送前和响应返回后修改数据。

5. 其他核心功能
  • 请求取消: 允许在请求未完成时取消它,以避免不必要的网络流量和资源占用。
  • 超时设置: 可以设置请求超时时间,防止请求长时间无响应。
  • CSRF 防护: 内置了对客户端跨站请求伪造(CSRF)的防护机制。
  • 更丰富的配置: 提供了全局配置、实例配置和单次请求配置,非常灵活。

axios的各种用法

🧩 函数分析

export const uploadImage = (
  file: File,
  options?: {
    onProgress?: (percent: number) => void;
    category?: string;
    folder?: string;
  }
): Promise<Result<ImageItem>> => {
  return upload<ImageItem>({
    url: '/upload',
    file,
    onProgress: options?.onProgress,
    data: {
      ...(options?.category && { category: options.category }),
      ...(options?.folder && { folder: options.folder }),
    },
  });
};
  • 第一个参数 file:就是你要上传的文件对象,一般是从 <input type="file"> 里获取的。

  • 第二个参数 options:是一个可选配置对象,包括:

    • onProgress: 上传进度回调函数(会不断被调用)。
    • category: 上传分类。
    • folder: 上传文件夹路径。

返回值是一个 Promise<Result<ImageItem>>,所以你可以用 await.then() 来接收结果。


✅ 调用示例

示例 1:最简单的调用

const file = input.files?.[0]; // 从 <input type="file"> 中获取

if (file) {
  const res = await uploadImage(file);
  console.log(res);
}

示例 2:带进度条的调用

const file = input.files?.[0];

if (file) {
  const res = await uploadImage(file, {
    onProgress: (percent) => {
      console.log("上传进度:", percent + "%");
    },
  });

  console.log("上传完成:", res);
}

示例 3:带分类和文件夹路径

const file = input.files?.[0];

await uploadImage(file, {
  category: "avatar",
  folder: "users/profile",
  onProgress: (percent) => {
    console.log(`上传进度:${percent}%`);
  },
});

示例 4:结合组件使用(React 举例)

function UploadAvatar() {
  const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const res = await uploadImage(file, {
      category: "avatar",
      onProgress: (percent) => {
        console.log("上传中:", percent, "%");
      },
    });

    console.log("上传结果:", res);
  };

  return <input type="file" onChange={handleChange} />;
}

💡总结:

调用方式说明
uploadImage(file)最简调用
uploadImage(file, { onProgress })带进度监听
uploadImage(file, { category, folder })指定分类和目录
await / .then()都可以接收返回结果

是否希望我也帮你补上 upload<T>() 的实现参考?那样你能完整地看到 uploadImage() 背后是怎么工作的。

你说:

讲讲axios的各种用法?

ChatGPT 说:

好!Axios 是前端最常用的 HTTP 请求库之一。
下面我会系统地讲解 Axios 的各种用法,包括:

🌱 基础使用 → 🌾 参数配置 → ⚙️ 拦截器 → 📦 并发请求 → 🧩 文件上传下载 → 🧠 封装与进阶技巧


🌱 一、基础用法

Axios 默认支持 Promise,所以最简单的方式如下:

import axios from "axios";

// GET 请求
axios.get("/api/user").then((res) => {
  console.log(res.data);
});

// POST 请求
axios.post("/api/login", { username: "admin", password: "123456" });

等价的 await 写法:

const res = await axios.get("/api/user");
console.log(res.data);

🌾 二、常用请求方式

axios.get("/api/list");                    // GET
axios.post("/api/add", { name: "Tom" });   // POST
axios.put("/api/update/1", { name: "Tom" }); // PUT
axios.delete("/api/delete/1");             // DELETE
axios.patch("/api/update-part/1", { age: 18 }); // PATCH

⚙️ 三、配置参数(config

Axios 所有方法都可以带一个配置对象。

axios({
  method: "post",
  url: "/api/upload",
  data: { name: "Alice" },
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer token",
  },
  timeout: 5000,
  params: { page: 1, size: 10 },
});

常用配置项:

配置项说明
url请求地址
method请求方式
baseURL基础路径
headers请求头
paramsURL 查询参数(GET)
data请求体数据(POST、PUT)
timeout超时时间(ms)
responseType响应类型(json / blob / arraybuffer
onUploadProgress上传进度回调
onDownloadProgress下载进度回调

🧰 四、创建实例(推荐用法)

一般我们会创建一个带基础配置的实例,这样更灵活:

const api = axios.create({
  baseURL: "https://api.example.com",
  timeout: 8000,
  headers: { "Content-Type": "application/json" },
});

// 使用实例
api.get("/user");
api.post("/login", { username: "xx" });

🧩 五、请求与响应拦截器

拦截器可以在请求发出前、响应到达后做统一处理:

api.interceptors.request.use(
  (config) => {
    // 比如自动带上 token
    const token = localStorage.getItem("token");
    if (token) config.headers.Authorization = `Bearer ${token}`;
    return config;
  },
  (error) => Promise.reject(error)
);

api.interceptors.response.use(
  (response) => {
    // 统一处理成功响应
    return response.data;
  },
  (error) => {
    // 统一处理错误
    console.error("请求错误:", error);
    return Promise.reject(error);
  }
);

📦 六、并发请求

同时发多个请求,用 axios.all

axios
  .all([axios.get("/user"), axios.get("/orders")])
  .then(axios.spread((user, orders) => {
    console.log(user.data, orders.data);
  }));

或更现代的写法:

const [user, orders] = await Promise.all([
  axios.get("/user"),
  axios.get("/orders"),
]);

📤 七、上传文件

const formData = new FormData();
formData.append("file", file);
formData.append("category", "avatar");

axios.post("/upload", formData, {
  headers: { "Content-Type": "multipart/form-data" },
  onUploadProgress: (e) => {
    const percent = Math.round((e.loaded * 100) / e.total);
    console.log("上传进度:", percent + "%");
  },
});

📥 八、下载文件

const res = await axios.get("/file/download", { responseType: "blob" });
const blob = new Blob([res.data]);
const url = URL.createObjectURL(blob);

const a = document.createElement("a");
a.href = url;
a.download = "example.pdf";
a.click();
URL.revokeObjectURL(url);

🧠 九、请求封装(实际项目中常见)

一般会封装成一个统一的请求方法:

const request = axios.create({
  baseURL: "/api",
  timeout: 10000,
});

request.interceptors.response.use(
  (res) => res.data,
  (err) => {
    console.error("接口错误:", err);
    return Promise.reject(err);
  }
);

// 使用封装
export const getUser = (id: number) => request.get(`/user/${id}`);
export const addUser = (data: any) => request.post("/user", data);

🔥 十、进阶技巧

✅ 取消请求

const controller = new AbortController();

axios.get("/api/data", { signal: controller.signal });

// 取消请求
controller.abort();

✅ 设置默认配置

axios.defaults.baseURL = "https://api.xxx.com";
axios.defaults.headers.common["Authorization"] = "Bearer token";

实战例子

axios 请求拦截器 config 的配置

    // 添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now(),
      };
    }

Axios 请求拦截器 config 中包含哪些参数?

🔥 最常用的参数(你写拦截器时基本只用这些)

参数类型作用
urlstring请求地址
methodstring请求方法(get/post/put/delete…)
baseURLstring基础地址
headersAxiosHeaders请求头对象
paramsanyGET 查询参数(会自动拼到 URL 上)
dataanyPOST/PUT/PATCH 的 body 数据
timeoutnumber请求超时(毫秒)
withCredentialsboolean是否携带 cookie
signalAbortSignal用于取消请求
transformRequestFunction发送前对 data 做处理
onUploadProgressFunction上传进度回调
onDownloadProgressFunction下载进度回调
axios 响应拦截器 response 的值
interface AxiosResponse<T = any>  {
  data: T;                // 服务器返回的数据
  status: number;         // HTTP 状态码 (200/400/500...)
  statusText: string;     // 状态文本 (OK / Not Found)  一般不用
  headers: AxiosResponseHeaders;  // 响应头
  config: InternalAxiosRequestConfig; // 当时的请求配置
  request?: any;          // 原生的请求对象(XMLHttpRequest)
}

axios 响应拦截器 错误的回调函数 error的属性有哪些?

🧩 总结(你最常用的其实只有这 4 个)

字段意义
error.response服务器返回的错误响应(如 400、500)
error.request请求发出但没响应(断网等)
error.code错误类型(超时、网络错误等)
error.config原请求配置(可用于重试)

🔥 1. error.response(最重要)

当服务器返回 4xx/5xx 时会有 response

结构和正常 response 一样:

error.response.data
error.response.status
error.response.headers
error.response.config

🔥 2. error.config(请求配置)

无论成功失败,这个一定有。

error.config.url
error.config.method
error.config.headers

经常用于 重新发请求(如 refresh token 重试):

const originalRequest = error.config;

🔥 3. error.code(错误类型字符串)

常见值:

error.code意义
ECONNABORTED超时
ERR_NETWORK网络错误(断网)
ERR_BAD_REQUEST4xx 之类
ERR_BAD_RESPONSE5xx 之类
ERR_CANCELED请求被取消(AbortController)

🔥 4. error.request(底层 XHR 对象)

浏览器中的原生 XMLHttpRequest

通常不用。

但可以用来判断是否完全没收到服务器响应

if (error.request && !error.response) {
  console.log("请求已发出,但服务器无响应");
}