前端学习:Axios Http请求库入门与实战应用

devtools/2025/2/6 9:10:00/

什么是Promise?

Promise 是一个表示异步操作最终完成或失败的对象。它允许你更优雅地处理异步操作,避免回调地狱(Callback Hell)。

特点:

  • 异步性:Promise 代表一个异步操作的最终完成或失败。

  • 不可更改:一旦 Promise 的状态确定为成功(resolved)或失败(rejected),它就不会再改变。

  • 链式调用:可以将多个异步操作按顺序排列,形成链式调用。

Promise的基本状态

一个 Promise 对象有三种状态:

状态含义
Pending异步操作尚未完成(初始状态)。
Fulfilled异步操作成功完成。
Rejected异步操作失败。

创建一个Promise

Promise 通过其构造函数创建,并且需要传递一个执行器(executor)函数。executor 函数接收两个参数:resolvereject

// 创建一个 Promise
const promise = new Promise((resolve, reject) => {// 异步操作setTimeout(() => {const success = true; // 假设操作成功if (success) {resolve("操作成功!"); // 处理成功结果} else {reject("操作失败!"); // 处理失败结果}}, 1000);
});

参数解释:

resolve: 标志异步操作成功,将 Promise 状态变为 Fulfilled

reject: 标志异步操作失败,将 Promise 状态变为 Rejected

resolvereject 是构造器内部定义的函数,会在 Promise 被创建时自动传递给执行器。你的任务是通过执行器的逻辑调用这些函数,告诉 Promise 操作成功还是失败。

resolve() 是一个由 Promise 构造器提供的函数,用于标记异步操作成功完成的状态。它是 Promise 执行器(executor)的一部分。当你在执行器中调用 resolve(value) 时,你会将一个值传递给它,这个值会作为 Promise 的成功结果。

const promise = new Promise((resolve) => {// 模拟异步操作setTimeout(() => {const successMessage = "操作成功";resolve(successMessage); // 将 "操作成功" 传递出去}, 1000);
});

.then() 方法的第一个参数是一个回调函数,用于处理 resolve() 的结果。这个回调函数会接收到 resolve() 传入的参数。

promise.then((result) => {console.log(result); // 输出: 操作成功}).catch((error) => {console.error(error);});

 

resolve() 可以传递任何类型的值,包括:

  • 基本数据类型(字符串、数字、布尔值等)。

  • 对象或数组。

  • 甚至可以传递另一个 Promise。

同理,reject函数用于传递Error错误的详细参数信息。reject() 是 Promise 中用于传递错误信息的机制。它的主要作用是告诉 Promise,异步操作失败了,并且可以携带具体的错误信息(如错误的原因、错误的详细描述等)。

使用Promise

Promise 的主要方法是 .then().catch(),它们分别用于处理成功和失败的结果。

.then() 方法

promise.then((result) => {console.log(result); // 输出:操作成功!},(error) => {console.error(error); // 如果失败,会执行这里});

then 方法用于处理 executor 中的成功结果。它可以接收两个回调函数:

  1. 第一个参数:处理成功的回调函数。

  2. 第二个参数(可选):处理失败的回调函数。

.catch() 方法

catch 方法专门用于处理失败的结果。

promise.then((result) => {console.log(result); // 输出:操作成功!}).catch((error) => {console.error(error); // 如果失败,会执行这里});

Promise的链式调用

Promise 支持链式调用,这有助于将多个异步操作按顺序排列。

function fetchUser() {return new Promise((resolve) => {setTimeout(() => {resolve({ id: 1, name: "John Doe" });}, 1000);});
}function fetchUserDetails(userId) {return new Promise((resolve) => {setTimeout(() => {resolve({ age: 30, email: "john@example.com" });}, 1000);});
}// 链式调用
fetchUser().then((user) => {console.log("用户信息:", user);return fetchUserDetails(user.id); // 返回一个新的 Promise}).then((details) => {console.log("用户详细信息:", details);}).catch((error) => {console.error("错误:", error);});

什么是 Axios?

Axios 是一个基于 Promise 的 HTTP 库,用于发送网络请求。它可以在浏览器和 Node.js 环境中使用,支持各种 HTTP 方法(如 GET, POST, PUT, DELETE 等)。它的主要特点包括:

  • 简单易用:API 设计直观,易于上手。

  • 支持 Promise:与现代 JavaScript 无缝集成。

  • 拦截器:可以在请求发送前和响应接收后自定义处理逻辑。

  • 取消请求:可以取消正在执行的请求。

  • 错误处理:提供统一的错误处理机制。

  • 支持请求和响应的序列化。

什么是 async/await

async/await 是 JavaScript 提供的用于处理异步操作的语法糖,它简化了异步代码的编写和阅读。它的本质是基于 Promise 的异步操作封装,使得异步代码可以像同步代码一样书写,避免了 .then() 链的嵌套问题。

核心特点:
  • async:修饰一个函数,表明该函数包含异步操作,会自动返回一个 Promise

  • await:用于等待一个 Promise 完成,只能在 async 函数内部使用。

// async/await
async function asyncFunction() {try {const result = await PromiseReturningFunction();return result;} catch (error) {console.error(error);}
}// 等同于以下 Promise 写法
function promiseFunction() {return PromiseReturningFunction().then(result => {return result;}).catch(error => {console.error(error);});
}
更方便链式调用和组合

async/await 允许将异步操作写成一个序列,而不用像 then() 那样通过链式调用来处理依赖关系。

// 使用 then()
fetchData1().then(data1 => {return fetchData2(data1);}).then(data2 => {return fetchData3(data2);}).then(data3 => {console.log(data3);});// 使用 async/await
async function handleData() {const data1 = await fetchData1();const data2 = await fetchData2(data1);const data3 = await fetchData3(data2);console.log(data3);
}

业务场景和函数类型建议使用 async/await 的情况 

需要处理多个异步操作

当你需要按顺序执行多个异步操作,并且每个操作的结果可能会影响下一个操作时,async/await 是最佳选择。

async function fetchData() {try {const user = await fetchUserById(1);const posts = await fetchPostsByUserId(user.id);console.log('User:', user);console.log('Posts:', posts);} catch (error) {console.error('Error:', error);}
}
需要分解异步流程的复杂逻辑

对于复杂的异步流程,async/await 可以将逻辑分解为多个函数,使代码更模块化。

async function downloadAndProcessFile(url) {const filePath = await downloadFile(url);const fileContent = await readFile(filePath);const processedData = processFile(fileContent);return processedData;
}

 

创建 Axios 实例

Axios项目结构

src/
├── utils/           # 工具模块
│   ├── axios.js     # 封装 Axios 实例
├── api/             # API 模块
│   ├── auth.js      # 用户认证 API
│   ├── user.js      # 用户相关 API
├── store/           # 状态管理
│   ├── auth.js      # 用户认证状态管理
│   ├── user.js      # 用户信息状态管理
├── components/      # UI 组件
├── App.vue          # 根组件

在前端项目中,Axios 相关的文件通常位于以下位置:

  1. 项目根目录

    • package.json:通过 npmyarn 添加 Axios 依赖后的配置文件。

    • node_modules:包含 Axios 的安装包。

  2. 工具模块

    • src/utils/axios.jssrc/utils/request.js:用于封装 Axios 实例,统一配置请求和响应拦截器。

  3. API 模块

    • src/api/auth.js:封装用户认证相关的 API。

    • src/api/user.js:封装用户相关的 API(如获取用户信息)。

    • src/api/service.js:封装服务相关的 API(如获取服务列表)。

  4. Vuex 或 Pinia 模块

    • 如果项目使用状态管理库(如 Vuex 或 Pinia),API 调用逻辑可能会出现在这些模块中。

使用Vue3+Pinia项目进行演示

src/api/axios.js

// src/utils/axios.js (修改后)
import axios from 'axios';const http = axios.create({baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',timeout: 10000,headers: { 'Content-Type': 'application/json' }
});// 请求拦截器优化
http.interceptors.request.use(config => {const accessToken = localStorage.getItem('accessToken');const refreshToken = localStorage.getItem('refreshToken');// 设置 accessToken 到请求头if (accessToken) {config.headers.Authorization = `Bearer ${accessToken}`;}// 如果是刷新 Token 的请求,将 refreshToken 放到自定义请求头if (config.url === '/auth/refresh-token' && refreshToken) {config.headers['X-Refresh-Token'] = refreshToken;}return config;
});// 响应拦截器优化
http.interceptors.response.use(response => response.data, // 自动解构 dataasync error => {const originalRequest = error.config;// 检查是否为 401 错误且未重试过if (error.response?.status === 401 && !originalRequest._retry) {originalRequest._retry = true; // 标记请求已重试try {// 使用独立实例避免拦截器循环const refreshClient = axios.create({baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',headers: {'X-Refresh-Token': localStorage.getItem('refreshToken') // 通过自定义请求头传递 refreshToken}});// 调用刷新 Token 接口const { data } = await refreshClient.post('/auth/refresh-token');// 更新本地存储的 TokenlocalStorage.setItem('accessToken', data.accessToken);localStorage.setItem('refreshToken', data.refreshToken);// 更新原始请求的 Authorization 头originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;// 重新发起原始请求return http(originalRequest);} catch (refreshError) {// 刷新 Token 失败,清除本地存储并跳转到登录页localStorage.clear();window.location.href = '/login'; // 使用路由跳转而非页面刷新return Promise.reject(refreshError);}}// 其他错误直接抛出return Promise.reject(error);}
);export default http;

接口服务层封装

// src/api/auth.js (保持良好)
import http from '@/utils/axios';export const login = credentials => http.post('/auth/login', credentials);
export const register = userData => http.post('/auth/register', userData);
export const refreshToken = () => http.post('/auth/refresh-token'); // 参数通过拦截器自动携带

登录使用双 Token 策略保证安全性

双 Token 策略通常包括:

  • Access Token:短时间有效,用于用户认证。

  • Refresh Token:长时间有效,用于刷新 Access Token。

Pinia Store

// src/store/auth.js (组合式 API 版本)
import { defineStore } from 'pinia';
import { login, register } from '@/api/auth';
import { useRouter } from 'vue-router';export const useAuthStore = defineStore('auth', () => {const router = useRouter();// Stateconst isAuthenticated = ref(!!localStorage.getItem('accessToken'));const accessToken = ref(localStorage.getItem('accessToken') || null);const refreshToken = ref(localStorage.getItem('refreshToken') || null);// Actionsconst handleTokenUpdate = (newAccessToken, newRefreshToken) => {accessToken.value = newAccessToken;refreshToken.value = newRefreshToken;localStorage.setItem('accessToken', newAccessToken);localStorage.setItem('refreshToken', newRefreshToken);isAuthenticated.value = true;};const loginAction = async (credentials) => {try {const data = await login(credentials);handleTokenUpdate(data.accessToken, data.refreshToken);} catch (error) {throw new Error('登录失败: ' + error.message); // 抛出错误供组件处理}};const registerAction = async (userData) => {try {await register(userData);await router.push('/login'); // 正确使用路由} catch (error) {throw new Error('注册失败: ' + error.message);}};const logout = () => {accessToken.value = null;refreshToken.value = null;isAuthenticated.value = false;localStorage.clear();router.push('/login');};return {isAuthenticated,accessToken,refreshToken,login: loginAction,register: registerAction,logout};
});

组件中调用

<!-- Login.vue -->
<template><!-- 保持原有模板内容,v-model 绑定保持不变 -->
</template><script setup>
import { ref } from 'vue'
import { useAuthStore } from '@/store/auth'const username = ref('')
const password = ref('')const handleLogin = () => {const authStore = useAuthStore()authStore.login({ username: username.value, password: password.value })
}
</script>
<!-- Register.vue -->
<template><!-- 保持原有模板内容,v-model 绑定保持不变 -->
</template><script setup>
import { ref } from 'vue'
import { useAuthStore } from '@/store/auth'const username = ref('')
const email = ref('')
const password = ref('')const handleRegister = () => {const authStore = useAuthStore()authStore.register({username: username.value,email: email.value,password: password.value})
}
</script>


http://www.ppmy.cn/devtools/156500.html

相关文章

udp和tcp的区别

目录 UDP 和 TCP 的区别 1. 连接性 2. 可靠性 3. 数据传输顺序 4. 流量控制和拥塞控制 5. 效率 6. 应用场景 UDP 和 TCP 的 C/C 代码实现区别 1. TCP 服务器端和客户端 TCP 服务器端&#xff08;Server&#xff09; TCP 客户端&#xff08;Client&#xff09; 2. U…

Vue.js 异步、延迟组件加载

Vue.js 异步、延迟组件加载 今天我们来聊聊 Vue 3 中的一个非常实用的优化技巧&#xff1a;异步/延迟组件加载。我们将通过 Vue 3 的一些语法糖来提升性能&#xff0c;特别是在渲染大量组件时&#xff0c;异步加载将大大减少页面的初次加载时间。 什么是异步组件加载&#xf…

Kafka分区策略实现

引言 Kafka 的分区策略决定了生产者发送的消息会被分配到哪个分区中&#xff0c;合理的分区策略有助于实现负载均衡、提高消息处理效率以及满足特定的业务需求。 轮询策略&#xff08;默认&#xff09; 轮询策略是 Kafka 默认的分区策略&#xff08;当消息没有指定键时&…

怎么获得小学、初中高中教材的pdf版【转载】

教材pdf下载步骤&#xff1a; 步骤操作概述示例图步骤一&#xff1a;进入官网进入 国家智慧教育公共服务平台&#xff1a;https://www.smartedu.cn/&#xff0c;进行登录或注册步骤二&#xff1a;选择教材点击中小学智慧教育&#xff0c;点击教材&#xff0c;选择自己喜欢的教…

使用 Docker Compose 一键启动 Redis、MySQL 和 RabbitMQ

目录 一、Docker Compose 简介 二、服务配置详解 1. Redis 配置 2. MySQL 配置 3. RabbitMQ 配置 三、数据持久化与时间同步 四、部署与管理 五、总结 目录挂载与卷映射的区别 现代软件开发中&#xff0c;微服务架构因其灵活性和可扩展性而备受青睐。为了支持微服务的…

33.Word:国家中长期人才发展规划纲要【33】

目录 NO1.2样式​ NO3​ 图表 ​ NO4.5.6​ 开始→段落标记视图→导航窗格→检查有无遗漏 NO1.2样式 F12/另存为&#xff1a;Word.docx&#xff1a;考生文件夹样式的复制样式的修改 样式的应用&#xff08;没有相似/超级多的情况下&#xff09;——替换 [ ]通配符&#x…

每日 Java 面试题分享【第 18 天】

欢迎来到每日 Java 面试题分享栏目&#xff01; 订阅专栏&#xff0c;不错过每一天的练习 今日分享 3 道面试题目&#xff01; 评论区复述一遍印象更深刻噢~ 目录 问题一&#xff1a;什么是 Java 中的双亲委派模型&#xff1f;问题二&#xff1a;Java 中 wait() 和 sleep()…

基于 Redis GEO 实现条件分页查询用户附近的场馆列表

&#x1f3af; 本文档详细介绍了如何使用Redis GEO模块实现场馆位置的存储与查询&#xff0c;以支持“附近场馆”搜索功能。首先&#xff0c;通过微信小程序获取用户当前位置&#xff0c;并将该位置信息与场馆的经纬度数据一同存储至Redis中。利用Redis GEO高效的地理空间索引能…