Ajax&Json

一、Ajax基础知识

这是一项非常重要的前端技术,用于创建动态、快速的网页应用。

1、Ajax 是什么?

Ajax (Asynchronous JavaScript and XML)即"异步的 JavaScript 与 XML ",是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容的技术。

  • 异步 (Asynchronous): 指发送请求后,不会阻塞浏览器,用户仍然可以操作页面,当服务器响应返回时,再由 JavaScript 处理响应结果。

  • JavaScript: 负责发起请求、处理响应的核心语言。

  • XML : 早期常用的数据交换格式,但现在更流行的是 JSON

核心价值 :提升用户体验,实现页面的局部刷新,避免页面"白屏"和闪烁。

2、工作原理

Ajax 的工作原理相当于在用户和服务器之间加了一个"中间层"(即 Ajax 引擎)。

  1. 用户触发事件(如点击按钮、滚动页面、输入内容等)。

  2. JavaScript 创建 XMLHttpRequest 对象

  3. 通过该对象向服务器发送异步请求

  4. 服务器处理请求,并返回数据(通常是 JSON 或 XML)。

  5. JavaScript 解析服务器返回的数据

  6. 使用 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()

  • 对于 POSTPUT 等请求:可以在 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、注意事项

  1. 跨域问题 (CORS) : 浏览器出于安全考虑,默认禁止 Ajax 跨域请求。需要服务器设置 Access-Control-Allow-Origin 等响应头来允许跨域。

  2. 错误处理 : 一定要做好错误处理(网络错误、服务器错误、解析错误等),使用 .catch() 或检查 status 码。

  3. 异步性 : 理解 Ajax 是异步 操作,代码不会等待请求完成再执行下一行。所有依赖于响应结果的操作都必须在 .then() 或回调函数中进行。

二、JSON入门

JSON 是现代 Web 开发中最重要的数据交换格式之一。

1、JSON 是什么?

JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。它采用完全独立于语言的文本格式,但使用了类似于 JavaScript 对象和数组的语法,使得人们很容易阅读和编写,同时也便于机器解析和生成。

核心特点:

  • 轻量级:相比于 XML,没有冗余的标签,体积更小,传输速度更快。

  • 易读性:结构清晰,层次分明,人类和机器都容易理解。

  • 语言无关:虽然源自 JavaScript,但几乎所有主流编程语言(Python, Java, C#, PHP, Go 等)都提供了生成和解析 JSON 的工具。

  • 自描述性:通过键值对的方式清晰地描述数据含义。

2、JSON 的语法规则

JSON 的语法可以看作是 JavaScript 对象和数组语法的子集,非常严格。主要有以下规则:

  1. 数据在名称/值对(键值对)中 : 例如 "name": "张三"

  2. 数据由逗号分隔 : 多个键值对用逗号 , 隔开。

  3. 花括号 {} 保存对象: 对象包含无序的键值对集合。

  4. 方括号 [] 保存数组: 数组包含有序的值集合。

  5. 字符串必须使用双引号 "" : 这是与 JavaScript 对象字面量最大的区别之一。键名也必须用双引号包裹

  6. 值可以是以下类型

    • 字符串(String) : 必须使用双引号。"Hello World"

    • 数字(Number) : 整数或浮点数。423.14

    • 布尔值(Boolean)truefalse

    • 空(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 的常见用途

  1. Web APIs 数据传输: 这是最常见的用途。前端通过 Ajax (Fetch API) 从服务器获取 JSON 格式的数据,然后动态更新页面。

  2. 配置文件 : 许多现代软件和框架(如 VS Code、Webpack、npm)使用 .json 文件作为配置文件(如 package.json, tsconfig.json)。

  3. NoSQL 数据库: 像 MongoDB 这样的数据库,其文档就是以类似 JSON 的格式(BSON)存储的。

  4. 序列化数据: 将程序中的对象状态转换为 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'
});

注意事项

  1. Content-Type:Axios 会自动设置合适的 Content-Type,但也可以手动覆盖

  2. 跨域请求:在浏览器中需要后端配置 CORS

  3. 取消请求:可以使用 CancelToken 来取消请求

  4. 文件上传:使用 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>
                    &nbsp;&nbsp;item.addEventListener('click', () => {<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;// 切换激活状态<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;menuItems.forEach(i => i.classList.remove('active'));<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;item.classList.add('active');<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;// 显示对应内容<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;const category = item.getAttribute('data-category');<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;document.querySelectorAll('.menu-content').forEach(content => {<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content.classList.remove('active');<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;});<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;document.getElementById(`${category}-content`).classList.add('active');<br>
                    &nbsp;&nbsp;});<br>
                    });
                </div>
            </div>
        </div>
        
        <footer>
            <p>二级联动菜单示例 &copy; 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!!!

相关推荐
2501_916013743 小时前
Web 抓包全指南 Web抓包工具、浏览器抓包方法、HTTPS 解密
前端·网络协议·ios·小程序·https·uni-app·iphone
海涛高软3 小时前
QT 两种库写法 LIBS += .a和LIBS += -L -l
前端·javascript·qt
好学且牛逼的马3 小时前
GO实战项目:流量统计系统完整实现(Go+XORM+MySQL + 前端)
前端·mysql·golang
Beginner x_u3 小时前
Vue 3 项目实战教程大事件管理系统 (一):从零开始搭建项目基础
前端·javascript·vue.js·pinia
aesthetician3 小时前
ahooks:一套高质量、可靠的 React Hooks 库
前端·react.js·前端框架
shizhenshide3 小时前
如何在同一站点支持多版本的 reCAPTCHA 的兼容性方案
服务器·前端·网络·安全·captcha·ezcaptcha
CodeCraft Studio3 小时前
借助Aspose.HTML控件,使用 Python 编程创建 HTML 页面
前端·python·html·aspose·python创建html·html sdk
杨超越luckly4 小时前
HTML应用指南:利用GET请求获取全国奥迪授权经销商门店位置信息
大数据·前端·python·html·数据可视化·门店数据
05Nuyoah4 小时前
DAY 01 HTML的认识
前端·html