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!!!

相关推荐
攀登的牵牛花2 分钟前
前端向架构突围系列 - 框架设计(二):糟糕的代码有哪些特点?
前端·架构
EndingCoder13 分钟前
函数基础:参数和返回类型
linux·前端·ubuntu·typescript
码客前端19 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛19 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程32 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保32 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫33 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
欧阳天风41 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder1 小时前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理1 小时前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活