一、Ajax基础知识
这是一项非常重要的前端技术,用于创建动态、快速的网页应用。
1、Ajax 是什么?
Ajax (Asynchronous JavaScript and XML)即"异步的 JavaScript 与 XML ",是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容的技术。
-
异步 (Asynchronous): 指发送请求后,不会阻塞浏览器,用户仍然可以操作页面,当服务器响应返回时,再由 JavaScript 处理响应结果。
-
JavaScript: 负责发起请求、处理响应的核心语言。
-
XML : 早期常用的数据交换格式,但现在更流行的是 JSON。
核心价值 :提升用户体验,实现页面的局部刷新,避免页面"白屏"和闪烁。
2、工作原理
Ajax 的工作原理相当于在用户和服务器之间加了一个"中间层"(即 Ajax 引擎)。
-
用户触发事件(如点击按钮、滚动页面、输入内容等)。
-
JavaScript 创建
XMLHttpRequest
对象。 -
通过该对象向服务器发送异步请求。
-
服务器处理请求,并返回数据(通常是 JSON 或 XML)。
-
JavaScript 解析服务器返回的数据。
-
使用 DOM 操作更新页面的局部内容。
https://www.w3schools.com/js/pic_ajax.gif
3、核心 API:XMLHttpRequest (XHR) 对象
这是实现 Ajax 最原始、最核心的 API。虽然现在有更现代的 fetch
API,但了解 XHR 是理解基础的关键。
创建 XHR 对象
javascript
const xhr = new XMLHttpRequest();
配置请求
使用 .open()
方法初始化一个请求。
javascript
xhr.open(method, url, async);
-
method
: 请求方法,如'GET'
、'POST'
、'PUT'
、'DELETE'
。 -
url
: 请求发送到的服务器地址。 -
async
(可选): 默认为true
,表示异步请求。一般很少用同步(false
)。
发送请求
使用 .send()
方法发送请求。
-
对于
GET
请求:通常无需发送数据,使用xhr.send(null)
或xhr.send()
。 -
对于
POST
、PUT
等请求:可以在send()
中发送数据,如xhr.send(JSON.stringify(data))
。
javascript
// GET 请求示例
xhr.open('GET', 'https://api.example.com/data', true);
xhr.send();
// POST 请求示例
xhr.open('POST', 'https://api.example.com/data', true);
xhr.setRequestHeader('Content-Type', 'application/json'); // 设置请求头,告诉服务器发送的是 JSON 数据
const data = { username: 'John', email: 'john@example.com' };
xhr.send(JSON.stringify(data));
处理响应
通过监听 onreadystatechange
事件来处理服务器返回的响应。
javascript
xhr.onreadystatechange = function() {
// readyState 表示请求的状态
if (xhr.readyState === 4) { // 4: 请求已完成,且响应已就绪
// status 是 HTTP 状态码,如 200 表示成功,404 表示未找到
if (xhr.status === 200) {
// 请求成功,处理响应数据
console.log(xhr.responseText); // 响应文本
// 如果返回的是 JSON,可以解析它
const data = JSON.parse(xhr.responseText);
// ... 使用 data 更新 DOM ...
} else {
// 请求出错
console.error('请求失败,状态码:', xhr.status);
}
}
};
readyState
的五个状态:
-
0: 请求未初始化 (
new XMLHttpRequest()
之后) -
1: 服务器连接已建立 (
open()
之后) -
2: 请求已接收 (
send()
之后,已收到响应头) -
3: 请求处理中(正在下载响应体)
-
4: 请求已完成,且响应已就绪
4、现代方法:Fetch API
fetch()
是 ES6 引入的现代、更强大的网络请求 API,它基于 Promise,语法更简洁、更易用。
基本用法
javascript
fetch(url, options) // options 是可选的配置项,用于设置 method, headers, body 等
.then(response => {
// 检查请求是否成功 (状态码在 200-299 之间)
if (!response.ok) {
throw new Error('网络请求失败: ' + response.status);
}
// 解析响应数据,例如解析为 JSON
return response.json();
})
.then(data => {
// 处理解析后的数据
console.log(data);
})
.catch(error => {
// 处理任何发生的错误(网络问题、解析错误等)
console.error('出错:', error);
});
示例:GET 请求
javascript
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
示例:POST 请求
javascript
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 告诉服务器发送的是 JSON
},
body: JSON.stringify({ // 将 JavaScript 对象转换为 JSON 字符串
username: 'John',
email: 'john@example.com'
})
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Fetch 的优势:
-
链式 Promise 调用,避免了回调地狱(Callback Hell)。
-
语法更清晰直观。
-
默认不携带 Cookie,需要显式配置。
5、实际应用示例:获取并显示用户列表
假设有一个返回用户列表的 API (https://api.example.com/users
)。
HTML:
html
<button id="loadUsers">加载用户列表</button>
<ul id="userList"></ul>
JavaScript (使用 Fetch):
javascript
document.getElementById('loadUsers').addEventListener('click', loadUsers);
function loadUsers() {
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error('获取用户失败');
}
return response.json();
})
.then(users => {
const userList = document.getElementById('userList');
userList.innerHTML = ''; // 清空现有列表
users.forEach(user => {
const li = document.createElement('li');
li.textContent = `${user.name} - ${user.email}`;
userList.appendChild(li);
});
})
.catch(error => {
console.error('Error:', error);
alert('加载数据时出错!');
});
}
6、关键总结

7、注意事项
-
跨域问题 (CORS) : 浏览器出于安全考虑,默认禁止 Ajax 跨域请求。需要服务器设置
Access-Control-Allow-Origin
等响应头来允许跨域。 -
错误处理 : 一定要做好错误处理(网络错误、服务器错误、解析错误等),使用
.catch()
或检查status
码。 -
异步性 : 理解 Ajax 是异步 操作,代码不会等待请求完成再执行下一行。所有依赖于响应结果的操作都必须在
.then()
或回调函数中进行。
二、JSON入门
JSON 是现代 Web 开发中最重要的数据交换格式之一。
1、JSON 是什么?
JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。它采用完全独立于语言的文本格式,但使用了类似于 JavaScript 对象和数组的语法,使得人们很容易阅读和编写,同时也便于机器解析和生成。
核心特点:
-
轻量级:相比于 XML,没有冗余的标签,体积更小,传输速度更快。
-
易读性:结构清晰,层次分明,人类和机器都容易理解。
-
语言无关:虽然源自 JavaScript,但几乎所有主流编程语言(Python, Java, C#, PHP, Go 等)都提供了生成和解析 JSON 的工具。
-
自描述性:通过键值对的方式清晰地描述数据含义。
2、JSON 的语法规则
JSON 的语法可以看作是 JavaScript 对象和数组语法的子集,非常严格。主要有以下规则:
-
数据在名称/值对(键值对)中 : 例如
"name": "张三"
-
数据由逗号分隔 : 多个键值对用逗号
,
隔开。 -
花括号
{}
保存对象: 对象包含无序的键值对集合。 -
方括号
[]
保存数组: 数组包含有序的值集合。 -
字符串必须使用双引号
""
: 这是与 JavaScript 对象字面量最大的区别之一。键名也必须用双引号包裹。 -
值可以是以下类型:
-
字符串(String) : 必须使用双引号。
"Hello World"
-
数字(Number) : 整数或浮点数。
42
或3.14
-
布尔值(Boolean) :
true
或false
-
空(Null) :
null
-
对象(Object) :
{ ... }
-
数组(Array) :
[ ... ]
-
3、JSON 值与 JavaScript 对比

⚠️ 重要区别:JSON 字符串必须用双引号!
// 正确的 JSON
{ "name": "Alice" }
// 错误的 JSON(键和值都用了单引号)
{ 'name': 'Alice' }
4、JSON 结构示例
1. 简单对象
描述一个人的信息。
{
"name": "张三",
"age": 28,
"isStudent": false,
"hobbies": ["阅读", "游泳", "编程"]
}
2. 嵌套对象
描述一个人及其地址信息。
{
"name": "李四",
"address": {
"street": "科技园路123号",
"city": "深圳市",
"postalCode": "518000"
},
"phoneNumbers": [
{
"type": "家庭",
"number": "0755-1234567"
},
{
"type": "工作",
"number": "0755-7654321"
}
]
}
3. 数组
描述一个用户列表。
[
{ "id": 1, "username": "user1", "email": "user1@example.com" },
{ "id": 2, "username": "user2", "email": "user2@example.com" },
{ "id": 3, "username": "user3", "email": "user3@example.com" }
]
5、在 JavaScript 中处理 JSON
浏览器和 Node.js 环境提供了全局的 JSON
对象,包含两个核心方法:
1. JSON.stringify()
将 JavaScript 对象或值转换为 JSON 字符串 。这个过程也叫序列化(Serialization)。
javascript
const person = {
name: "王五",
age: 25,
isStudent: true,
hobbies: ["音乐", "旅行"],
address: {
city: "北京"
}
};
// 将 JavaScript 对象转换为 JSON 字符串
const jsonString = JSON.stringify(person);
console.log(jsonString);
// 输出: {"name":"王五","age":25,"isStudent":true,"hobbies":["音乐","旅行"],"address":{"city":"北京"}}
// 可选参数:用于格式化输出,便于阅读
const prettyJsonString = JSON.stringify(person, null, 2);
console.log(prettyJsonString);
/* 输出(带缩进):
{
"name": "王五",
"age": 25,
"isStudent": true,
"hobbies": [
"音乐",
"旅行"
],
"address": {
"city": "北京"
}
}
*/
2. JSON.parse()
将 JSON 字符串解析为 JavaScript 对象或值 。这个过程也叫反序列化(Deserialization)。
javascript
// 假设我们从服务器或文件接收到一个 JSON 字符串
const jsonStringFromServer = '{"username":"john_doe","score":95,"passed":true}';
// 将 JSON 字符串解析为 JavaScript 对象
const userData = JSON.parse(jsonStringFromServer);
console.log(userData);
// 输出: {username: 'john_doe', score: 95, passed: true}
console.log(userData.username); // 输出: john_doe
console.log(userData.score + 5); // 输出: 100 (可以进行数学运算)
⚠️ 注意: 如果传入的字符串不是有效的 JSON,JSON.parse()
会抛出错误,因此最好用 try...catch
包裹。
javascript
try {
const obj = JSON.parse('无效的 JSON字符串');
} catch (error) {
console.error('解析 JSON 失败:', error);
}
6、JSON 的常见用途
-
Web APIs 数据传输: 这是最常见的用途。前端通过 Ajax (Fetch API) 从服务器获取 JSON 格式的数据,然后动态更新页面。
-
配置文件 : 许多现代软件和框架(如 VS Code、Webpack、npm)使用
.json
文件作为配置文件(如package.json
,tsconfig.json
)。 -
NoSQL 数据库: 像 MongoDB 这样的数据库,其文档就是以类似 JSON 的格式(BSON)存储的。
-
序列化数据: 将程序中的对象状态转换为 JSON 字符串,以便存储到文件或通过网络传输。
7、总结与最佳实践

JSON 非常简单但极其强大,是前后端交互的"通用语言"。掌握它的语法和如何在 JavaScript 中使用它是现代 Web 开发者的必备技能。
三、Ajax应用实践
1、Ajax+JSON开发模式

Ajax + JSON 开发模式。这是现代 Web 开发中最核心、最常用的前后端交互模式。
1、模式概述
Ajax + JSON 模式 = 异步通信 (Ajax) + 数据格式 (JSON)
-
Ajax : 负责在浏览器和服务器之间进行异步通信
-
JSON : 作为通信的数据交换格式
这种模式实现了前后端分离,让前端专注于展示和交互,后端专注于数据处理和业务逻辑。
2、工作流程

2、Jackson
这是 Java 生态中最流行、功能最强大的 JSON 处理库。
1、Jackson 是什么?
Jackson 是一个用于 Java 的高性能 JSON 处理库,主要功能包括:
-
序列化:将 Java 对象转换为 JSON 字符串
-
反序列化:将 JSON 字符串转换为 Java 对象
-
数据绑定:在 JSON 和 Java 对象之间建立映射关系
-
流式 API:高性能的读写操作
-
注解支持:通过注解控制序列化/反序列化行为
2、核心模块
XML
<!-- Maven 依赖 -->
<dependencies>
<!-- 核心库 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.0</version>
</dependency>
<!-- 注解支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.15.0</version>
</dependency>
<!-- 数据绑定 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<!-- Java 8 日期时间支持 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.0</version>
</dependency>
</dependencies>
3、基础用法
1. 创建 ObjectMapper
ObjectMapper
是 Jackson 的核心类,负责序列化和反序列化。
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class JacksonExample {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
// 注册 Java 8 日期时间模块
objectMapper.registerModule(new JavaTimeModule());
// 其他配置...
}
}
2. 基本序列化/反序列化
User 类:
java
public class User {
private Long id;
private String name;
private String email;
private int age;
private boolean active;
private LocalDateTime createdAt;
// 构造方法、getter、setter、toString 等
public User() {}
public User(Long id, String name, String email, int age, boolean active) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
this.active = active;
this.createdAt = LocalDateTime.now();
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}
基本操作:
java
public class BasicExample {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
// 创建用户对象
User user = new User(1L, "张三", "zhangsan@example.com", 25, true);
// 1. 序列化:对象 → JSON 字符串
String jsonString = objectMapper.writeValueAsString(user);
System.out.println("序列化结果: " + jsonString);
// 输出: {"id":1,"name":"张三","email":"zhangsan@example.com","age":25,"active":true,"createdAt":"2023-10-01T10:30:00"}
// 2. 反序列化:JSON 字符串 → 对象
String inputJson = "{\"id\":2,\"name\":\"李四\",\"email\":\"lisi@example.com\",\"age\":30,\"active\":false}";
User parsedUser = objectMapper.readValue(inputJson, User.class);
System.out.println("反序列化结果: " + parsedUser.getName());
// 3. 序列化到文件
objectMapper.writeValue(new File("user.json"), user);
// 4. 从文件反序列化
User fileUser = objectMapper.readValue(new File("user.json"), User.class);
System.out.println("文件反序列化: " + fileUser.getName());
// 5. 美化输出(格式化)
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
System.out.println("美化输出:\n" + prettyJson);
}
}
4、高级特性
1. 注解的使用
Jackson 提供了丰富的注解来控制序列化/反序列化行为。
注解示例:
java
import com.fasterxml.jackson.annotation.*;
import java.time.LocalDateTime;
@JsonInclude(JsonInclude.Include.NON_NULL) // 不序列化 null 值
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性
public class User {
@JsonProperty("user_id") // 自定义 JSON 字段名
private Long id;
@JsonProperty("user_name")
private String name;
@JsonIgnore // 忽略此字段
private String password;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
@JsonInclude(JsonInclude.Include.NON_DEFAULT) // 默认值不序列化
private int age;
// 构造方法、getter、setter...
}
2. 集合和数组的处理
java
public class CollectionExample {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
// List 序列化/反序列化
List<User> users = Arrays.asList(
new User(1L, "张三", "zhangsan@example.com", 25, true),
new User(2L, "李四", "lisi@example.com", 30, false)
);
String usersJson = objectMapper.writeValueAsString(users);
System.out.println("用户列表JSON: " + usersJson);
// 反序列化回 List
List<User> parsedUsers = objectMapper.readValue(usersJson,
objectMapper.getTypeFactory().constructCollectionType(List.class, User.class));
// Map 的处理
Map<String, User> userMap = new HashMap<>();
userMap.put("user1", new User(1L, "张三", "zhangsan@example.com", 25, true));
userMap.put("user2", new User(2L, "李四", "lisi@example.com", 30, false));
String mapJson = objectMapper.writeValueAsString(userMap);
System.out.println("用户Map JSON: " + mapJson);
}
}
3. 日期时间处理
java
public class DateTimeExample {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
public static void main(String[] args) throws Exception {
User user = new User();
user.setId(1L);
user.setName("测试用户");
user.setCreatedAt(LocalDateTime.now());
String json = objectMapper.writeValueAsString(user);
System.out.println("日期时间序列化: " + json);
}
}
5、实战应用:Spring Boot 集成
在 Spring Boot 中,Jackson 是默认的 JSON 处理器。
1. Controller 中的使用
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user); // 自动序列化为 JSON
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
// @RequestBody 自动反序列化 JSON 为 User 对象
User savedUser = userService.saveUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users); // 自动处理集合序列化
}
}
2. 自定义 Jackson 配置
java
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 配置选项
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 美化输出(仅开发环境)
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objectMapper;
}
}
3. 异常处理
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(JsonProcessingException.class)
public ResponseEntity<ErrorResponse> handleJsonException(JsonProcessingException ex) {
ErrorResponse error = new ErrorResponse("JSON处理错误", ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorResponse> handleMessageNotReadable(HttpMessageNotReadableException ex) {
ErrorResponse error = new ErrorResponse("请求体格式错误", "请检查JSON格式");
return ResponseEntity.badRequest().body(error);
}
}
// 错误响应类
public class ErrorResponse {
private String message;
private String details;
private LocalDateTime timestamp;
public ErrorResponse(String message, String details) {
this.message = message;
this.details = details;
this.timestamp = LocalDateTime.now();
}
// getter 方法
}
6、高级技巧
1. 自定义序列化器/反序列化器
java
// 自定义序列化器
public class CustomDateSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(value.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")));
}
}
// 自定义反序列化器
public class CustomDateDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
return LocalDateTime.parse(p.getText(),
DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"));
}
}
// 在实体类中使用
public class User {
@JsonSerialize(using = CustomDateSerializer.class)
@JsonDeserialize(using = CustomDateDeserializer.class)
private LocalDateTime createdAt;
}
2. 多态类型处理
java
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public abstract class Animal {
private String name;
}
public class Dog extends Animal {
private String breed;
}
public class Cat extends Animal {
private boolean indoor;
}
3、Ajax组件库-axios
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。它提供了简洁的 API 来处理 HTTP 请求,支持请求/响应拦截、自动转换 JSON 数据等强大功能。
axios官方学习文档:http://www.axios-js.com/
安装
使用 npm
bash
npm install axios
使用 CDN
html
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
基本用法
发起 GET 请求
javascript
// 获取所有用户
axios.get('/api/users')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('请求失败:', error);
});
// 使用 async/await
async function getUsers() {
try {
const response = await axios.get('/api/users');
console.log(response.data);
} catch (error) {
console.error('请求失败:', error);
}
}
发起 POST 请求
javascript
// 创建新用户
axios.post('/api/users', {
name: '张三',
email: 'zhangsan@example.com'
})
.then(response => {
console.log('用户创建成功:', response.data);
})
.catch(error => {
console.error('创建失败:', error);
});
发起 PUT 和 DELETE 请求
javascript
// 更新用户
axios.put('/api/users/1', {
name: '李四',
email: 'lisi@example.com'
});
// 删除用户
axios.delete('/api/users/1');
配置选项
请求配置
javascript
axios({
method: 'post',
url: '/api/users',
data: {
name: '王五'
},
timeout: 5000, // 超时时间
headers: {
'Content-Type': 'application/json'
}
});
默认配置
javascript
// 设置默认配置
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = 'Bearer token';
axios.defaults.timeout = 5000;
// 创建实例并配置
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000
});
响应结构
Axios 响应包含以下属性:
javascript
{
data: {}, // 服务器返回的数据
status: 200, // HTTP 状态码
statusText: 'OK', // HTTP 状态消息
headers: {}, // 响应头
config: {} // 请求配置
}
错误处理
javascript
axios.get('/api/users')
.catch(error => {
if (error.response) {
// 服务器返回了错误状态码
console.log(error.response.status);
console.log(error.response.data);
} else if (error.request) {
// 请求已发出但没有收到响应
console.log(error.request);
} else {
// 其他错误
console.log('Error', error.message);
}
});
拦截器
请求拦截器
javascript
axios.interceptors.request.use(
config => {
// 在发送请求前做些什么
config.headers.Authorization = 'Bearer token';
return config;
},
error => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
响应拦截器
javascript
axios.interceptors.response.use(
response => {
// 对响应数据做点什么
return response;
},
error => {
// 对响应错误做点什么
if (error.response.status === 401) {
// 处理未授权错误
}
return Promise.reject(error);
}
);
并发请求
javascript
// 同时发起多个请求
axios.all([
axios.get('/api/users'),
axios.get('/api/posts')
])
.then(axios.spread((usersResponse, postsResponse) => {
// 两个请求都完成后执行
console.log('Users:', usersResponse.data);
console.log('Posts:', postsResponse.data);
}));
实际应用示例
封装 API 请求
javascript
// api.js
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
});
// 添加请求拦截器
api.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
// 用户相关 API
export const userAPI = {
getUsers: () => api.get('/users'),
getUser: (id) => api.get(`/users/${id}`),
createUser: (userData) => api.post('/users', userData),
updateUser: (id, userData) => api.put(`/users/${id}`, userData),
deleteUser: (id) => api.delete(`/users/${id}`)
};
在组件中使用
javascript
import { userAPI } from './api';
// 获取用户列表
userAPI.getUsers()
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('获取用户失败:', error);
});
// 创建新用户
userAPI.createUser({
name: '赵六',
email: 'zhaoliu@example.com'
});
注意事项
-
Content-Type:Axios 会自动设置合适的 Content-Type,但也可以手动覆盖
-
跨域请求:在浏览器中需要后端配置 CORS
-
取消请求:可以使用 CancelToken 来取消请求
-
文件上传:使用 FormData 来处理文件上传
取消请求
javascript
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/api/users', {
cancelToken: source.token
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('请求已取消:', error.message);
}
});
// 取消请求
source.cancel('操作被用户取消');
1、Ajax的同步与异步的区别
核心概念一句话总结
-
异步(Async) :"不用等" 。浏览器发起 Ajax 请求后,不会冻结页面(UI),用户可以继续操作。请求完成后,会通过回调函数、Promise 或 Async/Await 来通知程序并处理结果。这是现在绝对推荐和使用的方式。
-
同步(Sync) :"必须等" 。浏览器发起 Ajax 请求后,会锁定 页面(UI),用户无法进行任何操作,直到请求完成并返回结果后,代码才会继续执行。这种方式已被废弃,强烈不推荐使用。
代码示例与流程分析
1. 异步(Asynchronous)示例
javascript
// 使用经典的 XMLHttpRequest (异步模式)
console.log('1. 请求开始前');
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true); // true 表示异步
xhr.onload = function() {
// 这是一个回调函数,会在请求成功完成后才被调用
if (xhr.status === 200) {
console.log('3. 请求成功,数据已收到: ', xhr.responseText);
}
};
xhr.send(); // 发起网络请求,但代码不会停在这里等待
// 这行代码不会等待请求结束,会立刻执行
console.log('2. 请求已发起,但代码继续执行,页面可操作');
// 输出顺序:
// 1. 请求开始前
// 2. 请求已发起,但代码继续执行,页面可操作
// 3. 请求成功,数据已收到: ...
异步执行流程图:

2. 同步(Synchronous)示例 (仅作了解,切勿使用)
javascript
console.log('1. 请求开始前');
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', false); // false 表示同步!
// 对于同步请求,可以在 send() 之后直接写处理逻辑,因为没有回调了
xhr.send(); // 代码会阻塞在这里,页面卡住,直到请求返回
// 下面的代码在请求完成前都不会执行
console.log('2. 请求已完成,数据: ', xhr.responseText); // 这里可以直接使用响应数据
// 输出顺序:
// 1. 请求开始前
// ...(页面卡住,等待请求)...
// 2. 请求已完成,数据: ...
同步执行流程图:

4、实现二级联动菜单
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>二级联动菜单</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px;
}
.container {
width: 100%;
max-width: 800px;
background-color: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
header {
background: linear-gradient(90deg, #3498db, #2980b9);
color: white;
padding: 20px;
text-align: center;
}
h1 {
font-size: 24px;
margin-bottom: 5px;
}
.description {
font-size: 14px;
opacity: 0.9;
}
.menu-container {
display: flex;
min-height: 400px;
}
.primary-menu {
width: 40%;
background-color: #f8f9fa;
border-right: 1px solid #eaeaea;
overflow-y: auto;
}
.secondary-menu {
width: 60%;
padding: 20px;
background-color: #fff;
}
.menu-title {
padding: 15px 20px;
font-weight: 600;
color: #2c3e50;
background-color: #e8f4fc;
border-bottom: 1px solid #eaeaea;
}
.menu-item {
padding: 15px 20px;
border-bottom: 1px solid #f1f1f1;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
justify-content: space-between;
align-items: center;
}
.menu-item:hover {
background-color: #e3f2fd;
}
.menu-item.active {
background-color: #d1e7ff;
border-right: 4px solid #3498db;
font-weight: 600;
}
.menu-item .arrow {
font-size: 12px;
color: #95a5a6;
}
.submenu-item {
padding: 12px 15px;
border-bottom: 1px solid #f9f9f9;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
}
.submenu-item:before {
content: "•";
margin-right: 10px;
color: #3498db;
}
.submenu-item:hover {
background-color: #f0f7ff;
padding-left: 20px;
}
.menu-content {
margin-top: 15px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 8px;
display: none;
}
.menu-content.active {
display: block;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.code-example {
background-color: #2d3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 6px;
margin-top: 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
overflow-x: auto;
}
footer {
text-align: center;
padding: 15px;
color: #7f8c8d;
font-size: 14px;
border-top: 1px solid #eaeaea;
}
@media (max-width: 600px) {
.menu-container {
flex-direction: column;
}
.primary-menu, .secondary-menu {
width: 100%;
}
.primary-menu {
border-right: none;
border-bottom: 1px solid #eaeaea;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>二级联动菜单</h1>
<p class="description">选择主菜单项查看相应的子菜单内容</p>
</header>
<div class="menu-container">
<div class="primary-menu">
<div class="menu-title">主菜单</div>
<div class="menu-item active" data-category="web">
<span>前端技术</span>
<span class="arrow">→</span>
</div>
<div class="menu-item" data-category="backend">
<span>后端技术</span>
<span class="arrow">→</span>
</div>
<div class="menu-item" data-category="database">
<span>数据库</span>
<span class="arrow">→</span>
</div>
<div class="menu-item" data-category="tools">
<span>开发工具</span>
<span class="arrow">→</span>
</div>
<div class="menu-item" data-category="framework">
<span>框架类库</span>
<span class="arrow">→</span>
</div>
</div>
<div class="secondary-menu">
<div class="menu-title">子菜单</div>
<div id="web-content" class="menu-content active">
<div class="submenu-item">HTML5</div>
<div class="submenu-item">CSS3</div>
<div class="submenu-item">JavaScript</div>
<div class="submenu-item">TypeScript</div>
<div class="submenu-item">响应式设计</div>
</div>
<div id="backend-content" class="menu-content">
<div class="submenu-item">Node.js</div>
<div class="submenu-item">Python</div>
<div class="submenu-item">Java</div>
<div class="submenu-item">PHP</div>
<div class="submenu-item">Ruby</div>
</div>
<div id="database-content" class="menu-content">
<div class="submenu-item">MySQL</div>
<div class="submenu-item">MongoDB</div>
<div class="submenu-item">PostgreSQL</div>
<div class="submenu-item">Redis</div>
<div class="submenu-item">SQLite</div>
</div>
<div id="tools-content" class="menu-content">
<div class="submenu-item">VS Code</div>
<div class="submenu-item">Git</div>
<div class="submenu-item">Webpack</div>
<div class="submenu-item">Docker</div>
<div class="submenu-item">Chrome DevTools</div>
</div>
<div id="framework-content" class="menu-content">
<div class="submenu-item">React</div>
<div class="submenu-item">Vue.js</div>
<div class="submenu-item">Angular</div>
<div class="submenu-item">Express</div>
<div class="submenu-item">Django</div>
</div>
<div class="code-example">
// JavaScript联动逻辑示例<br>
const menuItems = document.querySelectorAll('.menu-item');<br>
menuItems.forEach(item => {<br>
item.addEventListener('click', () => {<br>
// 切换激活状态<br>
menuItems.forEach(i => i.classList.remove('active'));<br>
item.classList.add('active');<br>
// 显示对应内容<br>
const category = item.getAttribute('data-category');<br>
document.querySelectorAll('.menu-content').forEach(content => {<br>
content.classList.remove('active');<br>
});<br>
document.getElementById(`${category}-content`).classList.add('active');<br>
});<br>
});
</div>
</div>
</div>
<footer>
<p>二级联动菜单示例 © 2023</p>
</footer>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const menuItems = document.querySelectorAll('.menu-item');
menuItems.forEach(item => {
item.addEventListener('click', () => {
// 移除所有激活状态
menuItems.forEach(i => i.classList.remove('active'));
// 添加当前项激活状态
item.classList.add('active');
// 获取数据类别
const category = item.getAttribute('data-category');
// 隐藏所有内容
document.querySelectorAll('.menu-content').forEach(content => {
content.classList.remove('active');
});
// 显示选中内容
document.getElementById(`${category}-content`).classList.add('active');
});
});
// 为子菜单项添加点击事件
const submenuItems = document.querySelectorAll('.submenu-item');
submenuItems.forEach(item => {
item.addEventListener('click', () => {
alert('您选择了: ' + item.textContent);
});
});
});
</script>
</body>
</html>

OVER!!!