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

news/2025/2/6 21:35:58/

什么是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/news/1569906.html

相关文章

查看设备uuid

在大多数操作系统中&#xff0c;可以通过不同的方式来查看设备的 UUID&#xff08;Universally Unique Identifier&#xff09;。以下是一些常见的方法&#xff1a; 在Linux系统中&#xff0c;可以使用命令行工具blkid或lsblk来查看设备的 UUID。例如&#xff0c;执行以下命令…

深度解析:网站快速收录与服务器性能的关系

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/37.html 网站快速收录与服务器性能之间存在着密切的关系。服务器作为网站运行的基础设施&#xff0c;其性能直接影响到搜索引擎对网站的抓取效率和收录速度。以下是对这一关系的深度解析&am…

ollama部署deepseek实操记录

1. 安装 ollama 1.1 下载并安装 官网 https://ollama.com/ Linux安装命令 https://ollama.com/download/linux curl -fsSL https://ollama.com/install.sh | sh安装成功截图 3. 开放外网访问 1、首先停止ollama服务&#xff1a;systemctl stop ollama 2、修改ollama的servic…

如何开发一个大语言模型,开发流程及需要的专业知识

开发大型语言模型&#xff08;LLM&#xff09;是一个复杂且资源密集的过程&#xff0c;涉及多个阶段和跨学科知识。以下是详细的开发流程和所需专业知识指南&#xff1a; 一、开发流程 1. 需求分析与规划 目标定义&#xff1a;明确模型用途&#xff08;如对话、翻译、代码生成…

音频录制一般在什么情况下会选择保存为PCM?什么情况会选择保存为WAV?

在音频开发中&#xff0c;选择保存为 PCM 或 WAV 格式取决于具体的应用场景和需求。以下是两种格式的特点以及适用场景的分析&#xff1a; PCM 格式 特点&#xff1a; 原始音频数据&#xff1a; PCM 是未压缩的原始音频数据&#xff0c;没有任何文件头或元数据。数据直接以二进…

013-51单片机红外遥控器模拟控制空调,自动制冷制热定时开关

主要功能是通过红外遥控器模拟控制空调&#xff0c;可以实现根据环境温度制冷和制热&#xff0c;能够通过遥控器设定温度&#xff0c;可以定时开关空调。 1.硬件介绍 硬件是我自己设计的一个通用的51单片机开发平台&#xff0c;可以根据需要自行焊接模块&#xff0c;这是用立创…

嵌入式(三)——嵌入式与人工智能关系_嵌入式人工智能的发展趋势

该篇文章结合了当下非常火热的人工智能问题&#xff0c;详细阐述了嵌入式与人工智能之间的关系&#xff0c;嵌入式如何与人工智能共同发展。 从人工智能与嵌入式的关系&#xff0c;人工智能和嵌入式发展历史的相关性以及嵌入式与人工智能未来的合作发展趋势。 人工智能&#x…

最新版Node.js下载安装指定版本图文版教程(非常详细)

文字目录 1、什么是Node.js&#xff1f;2、什么是 npm&#xff1f;3、下载Node.js安装包4、详细安装步骤 1、什么是Node.js&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;它使 JavaScript 可以在服务器端运行。在 Node.js 出现之前&#…