文章目录
- 异步编程
-
- [1. TypeScript 中的异步编程](#1. TypeScript 中的异步编程)
-
- [1.1 基本用法](#1.1 基本用法)
-
- 简单的异步函数
- 异步操作与延迟
- 并行执行多个异步操作
- 处理错误
- [使用 Promise.all 处理多个异步操作的错误](#使用 Promise.all 处理多个异步操作的错误)
- [2. 在 TypeScript 中使用 Vite 和 FastAPI](#2. 在 TypeScript 中使用 Vite 和 FastAPI)
-
- [2.1 安装 Node.js](#2.1 安装 Node.js)
- [2.2 创建 Vue 3 + TypeScript 项目](#2.2 创建 Vue 3 + TypeScript 项目)
- [2.3 安装依赖](#2.3 安装依赖)
- [2.4 启动开发服务器](#2.4 启动开发服务器)
- [2.5 单文件组件](#2.5 单文件组件)
-
- [HelloWorld.vue 示例](#HelloWorld.vue 示例)
- [2.6 FastAPI 后端](#2.6 FastAPI 后端)
-
- [安装 FastAPI](#安装 FastAPI)
- [交互式 API 文档](#交互式 API 文档)
- [2.7 发起 POST 请求](#2.7 发起 POST 请求)
-
- [修改后的 HelloWorld.vue](#修改后的 HelloWorld.vue)
- [更新后的 FastAPI 后端](#更新后的 FastAPI 后端)
- [2.8 测试](#2.8 测试)
- [3. 封装 Axios 的必要性与实现](#3. 封装 Axios 的必要性与实现)
-
- [3.1 为什么要封装 Axios](#3.1 为什么要封装 Axios)
- [3.2 Axios 类型声明](#3.2 Axios 类型声明)
-
- [封装 Axios 实例](#封装 Axios 实例)
- [3.3 创建 API 管理文件](#3.3 创建 API 管理文件)
-
- [type.ts 示例](#type.ts 示例)
- [api.ts 示例](#api.ts 示例)
- [3.5 在组件中使用 API](#3.5 在组件中使用 API)
-
- [使用方式一:顶层 `await`](#使用方式一:顶层
await
) - [使用方式二:函数内 `async/await`](#使用方式二:函数内
async/await
)
- [使用方式一:顶层 `await`](#使用方式一:顶层
- 学习总结
异步编程
1. TypeScript 中的异步编程
在 TypeScript 中,异步编程通常通过 async
和 await
关键字来实现,这使得处理异步操作更加优雅和直观。这些特性是基于 ES2017(ES8)标准的,允许我们编写可读性更强的异步代码。
1.1 基本用法
简单的异步函数
首先,我们定义一个简单的异步函数,它返回一个 Promise
对象:
typescript
async function greet(): Promise<string> {
return Promise.resolve("Hello, async world!");
}
async function execute() {
const message = await greet();
console.log(message); // 输出: Hello, async world!
}
execute();
在这个例子中,greet
函数是异步的,返回一个 Promise
。execute
函数使用 await
来获取 greet
函数的结果。
异步操作与延迟
我们可以模拟一个延迟操作,例如从服务器获取数据:
typescript
async function fetchData(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => resolve(42), 500); // 500 毫秒后返回 42
});
}
async function run() {
const data = await fetchData();
console.log(data); // 输出: 42
}
run();
这里的 fetchData
函数在 500 毫秒后解决,并返回一个数字。
并行执行多个异步操作
我们可以并行执行多个异步函数,使用 Promise.all
:
typescript
async function fetchFirstValue(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => resolve(10), 1000);
});
}
async function fetchSecondValue(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => resolve(20), 1000);
});
}
async function execute() {
const [first, second] = await Promise.all([fetchFirstValue(), fetchSecondValue()]);
console.log(first + second); // 输出: 30
}
execute();
在这个示例中,两个异步操作同时执行,最后将它们的结果相加。
处理错误
可以通过 try...catch
块来捕获异步函数中的错误:
typescript
async function faultyOperation(): Promise<number> {
throw new Error("An error occurred");
}
async function run() {
try {
const result = await faultyOperation();
} catch (error) {
console.error(error.message); // 输出: An error occurred
}
}
run();
这里,faultyOperation
函数抛出一个错误,我们在 run
函数中捕获并处理它。
使用 Promise.all 处理多个异步操作的错误
如果多个异步操作中有一个失败,可以使用 Promise.all
来处理:
typescript
async function failOperation(): Promise<number> {
return new Promise((_, reject) => {
setTimeout(() => reject('Operation failed'), 300);
});
}
async function successOperation(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => resolve(25), 300);
});
}
async function run() {
try {
const [result1, result2] = await Promise.all([failOperation(), successOperation()]);
console.log(result1 + result2);
} catch (error) {
console.error(error); // 输出: Operation failed
}
}
run();
在这个示例中,failOperation
将会拒绝,而 Promise.all
会立即捕获这个拒绝,并在 run
函数中处理。
2. 在 TypeScript 中使用 Vite 和 FastAPI
2.1 安装 Node.js
确保你已经安装了 Node.js(推荐使用最新的稳定版)。然后在命令行中运行以下命令来全局安装 Vite:
bash
npm install -g create-vite
2.2 创建 Vue 3 + TypeScript 项目
使用 Vite 创建一个新的 Vue 3 + TypeScript 项目,运行以下命令并替换 vue3ts
为你的项目名称:
bash
create-vite vue3ts --template vue-ts
2.3 安装依赖
进入项目目录并安装依赖:
bash
cd vue3ts
npm install
2.4 启动开发服务器
启动开发服务器:
bash
npm run dev
在浏览器中输入 http://localhost:5173/
,如果能看到页面,则说明环境搭建成功。
2.5 单文件组件
打开文件 src/components/HelloWorld.vue
,我们将开始修改这个单文件组件。
HelloWorld.vue 示例
html
<template>
<button @click="get_query()">发起GET请求</button>
</template>
<script setup lang='ts'>
import axios from 'axios';
interface TestData {
message: string;
}
const get_query = () => {
// 发起 GET 请求
axios.get<TestData>('http://127.0.0.1:8009/')
.then(response => {
const testData: TestData = response.data;
console.log(testData.message);
})
.catch(error => {
console.error(error);
});
}
</script>
2.6 FastAPI 后端
确保你使用 Python 3.8 以上版本(推荐 Python 3.10.5)。检查 Python 版本:
python
import sys
print(sys.version)
安装 FastAPI
安装 FastAPI:
bash
pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple
FastAPI 会自动安装 Uvicorn。创建一个 test.py
文件并粘贴以下代码:
python
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"]
)
@app.get("/")
async def root():
return {"message": "Hello World"}
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8009)
在 test.py
所在目录打开终端,运行:
bash
python test.py
访问地址为 http://127.0.0.1:8009/
,你将看到如下 JSON 响应:
json
{"message": "Hello World"}
交互式 API 文档
在浏览器中输入 http://127.0.0.1:8009/docs
,查看自动生成的交互式 API 文档。
2.7 发起 POST 请求
修改 HelloWorld.vue
,实现 POST 请求的功能。
修改后的 HelloWorld.vue
html
<template>
<button @click="get_query()">发起POST请求</button>
</template>
<script setup lang='ts'>
import axios from 'axios';
interface TestData {
item_id: string;
name: string;
}
const get_query = () => {
// 发起 POST 请求
axios.post<TestData>('http://127.0.0.1:8009/items/', { item_id: "5", name: "hello" })
.then(response => {
const testData: TestData = response.data;
console.log(testData.item_id);
console.log(testData.name);
})
.catch(error => {
console.error(error);
});
}
</script>
更新后的 FastAPI 后端
python
from fastapi import Body, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from typing import Annotated
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"]
)
@app.post("/items/")
async def read_item(item_id: Annotated[int, Body()], name: Annotated[str, Body()]):
return {"item_id": item_id, "name": name}
2.8 测试
按 F12 打开浏览器控制台,点击按钮后,控制台将看到输出:
5
hello
这样,你的前端与后端交互已成功实现。
3. 封装 Axios 的必要性与实现
3.1 为什么要封装 Axios
封装 Axios 主要是为了利用 TypeScript 的优势,确保类型检查和语法提示,从而提高代码的安全性和便捷性。具体而言,封装的好处包括:
-
统一处理请求头:通过创建 Axios 实例,可以集中管理请求头,避免每次请求都重复设置。
-
接口统一管理:将所有接口集中管理,使代码更清晰,易于维护,避免重复的代码。
-
避免回调地狱 :使用
async/await
语法,提高代码的可读性,避免嵌套的.then()
结构。
3.2 Axios 类型声明
Axios 提供了完备的类型声明,允许我们在 TypeScript 中准确地定义请求和响应的数据类型。核心方法如 request
、get
和 post
都支持泛型,从而使得我们可以指定响应数据的类型。
封装 Axios 实例
在 src
文件夹下创建一个 request
文件夹,并添加 base.ts
文件:
typescript
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
baseURL: '', // 请求地址前缀
timeout: 80000, // 请求超时时间
withCredentials: true, // 异步请求携带 cookie
});
// 请求拦截器
instance.interceptors.request.use(
config => {
// 可在此添加 token
return config;
},
error => Promise.reject(error)
);
// 响应拦截器
instance.interceptors.response.use(
response => response.data,
error => {
if (error.response.status === 401) {
localStorage.removeItem("x-auth-token");
// 可在此跳转登录页
}
return Promise.reject(error);
}
);
export default instance;
3.3 创建 API 管理文件
在 request
文件夹下,为不同模块创建文件夹,比如 user
,并添加 type.ts
和 api.ts
文件。
type.ts 示例
typescript
export interface ReqLogin {
username: string;
password: string;
}
export interface ProData {
projects?: string;
detail?: { code: number; message: string; data: string };
}
export interface ItypeAPI {
id: number;
username: string;
email: string;
token: string;
}
api.ts 示例
typescript
import instance from "../base";
import { ReqLogin, ProData, ItypeAPI } from "./type";
const headers = { "content-type": "application/x-www-form-urlencoded" };
// 登录请求
export const loginAPI = (data: ReqLogin): Promise<ItypeAPI> =>
instance.post("/v1/users/token", data, { headers });
export const checkAPI = (): Promise<ProData> =>
instance.get("/v1/users/pro", { headers });
3.5 在组件中使用 API
使用方式一:顶层 await
html
<script setup lang="ts">
import { checkAPI } from "../../request/api";
let res = await checkAPI();
console.log(res);
</script>
使用方式二:函数内 async/await
html
<script setup lang="ts">
import { loginAPI } from '../request/user/api';
const data = { username: "mockuser", password: "123456" };
const get_query = async () => {
let res = await loginAPI(data);
console.log(res);
};
</script>
通过这样的封装,可以有效地提升代码的可读性和维护性,同时利用 TypeScript 提供的强类型检查,确保数据的安全性。
学习总结
使用 async
和 await
,我们可以以一种更简洁和可读的方式编写异步代码。通过捕获错误和并行处理多个操作,我们可以确保代码的稳定性和可靠性。
最后一章居然讲解了axiso的源码,我哭死。本人亦因此开启了阅读前端框架源码的习惯。通过封装axiso,可以有效地提升代码的可读性和维护性,同时利用 TypeScript 提供的强类型检查,确保数据的安全性。