编程 抛弃 Ajax:拥抱更简洁强大的 Fetch API

2025-05-09 09:31:56 +0800 CST views 11

抛弃 Ajax:拥抱更简洁强大的 Fetch API

传统 Ajax 的繁琐实现

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      var response = JSON.parse(xhr.responseText);
      console.log(response);
    } else {
      console.error('请求失败,状态码:', xhr.status);
    }
  }
};
xhr.onerror = function() {
  console.error('请求出错');
};
xhr.send();

Fetch API 基础用法

基本 GET 请求

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('网络响应不正常');
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('请求出错:', error));

使用 async/await 语法

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('获取数据失败:', error);
  }
}

fetchData();

请求配置

POST 请求示例

fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your_token_here'
  },
  body: JSON.stringify({
    name: 'John Doe',
    email: 'john@example.com'
  })
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));

发送 FormData

const formData = new FormData();
formData.append('username', 'john');
formData.append('avatar', fileInput.files[0]);

fetch('https://api.example.com/profile', {
  method: 'POST',
  body: formData
})
.then(response => response.json())
.then(result => console.log('Success:', result))
.catch(error => console.error('Error:', error));

高级功能

流式响应处理

Fetch API 提供了多种方法来处理不同类型的响应数据:

  1. JSON 数据 - 最常用的格式,适合 REST API
fetch('https://api.example.com/data.json')
  .then(response => response.json())
  .then(data => console.log(data));
  1. 文本数据 - 处理纯文本响应
fetch('https://api.example.com/data.txt')
  .then(response => response.text())
  .then(text => console.log(text));
  1. 二进制数据 (Blob) - 处理图像、文件等
fetch('https://api.example.com/image.png')
  .then(response => response.blob())
  .then(blob => {
    const imgUrl = URL.createObjectURL(blob);
    document.getElementById('myImg').src = imgUrl;
  });
  1. ArrayBuffer - 处理原始二进制数据
fetch('https://api.example.com/data.bin')
  .then(response => response.arrayBuffer())
  .then(buffer => {
    // 处理二进制数据
    const view = new DataView(buffer);
    console.log(view.getInt32(0));
  });
  1. FormData - 处理表单数据响应
fetch('https://api.example.com/form')
  .then(response => response.formData())
  .then(formData => {
    console.log(formData.get('username'));
  });

中断请求

const controller = new AbortController();
const signal = controller.signal;

// 设置5秒超时
const timeoutId = setTimeout(() => controller.abort(), 5000);

fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => {
    clearTimeout(timeoutId);
    console.log(data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('请求被中止');
    } else {
      console.error('请求出错:', error);
    }
  });

并行请求

Promise.all([
  fetch('https://api.example.com/users'),
  fetch('https://api.example.com/posts')
])
.then(async ([usersResponse, postsResponse]) => {
  const users = await usersResponse.json();
  const posts = await postsResponse.json();
  return { users, posts };
})
.then(data => console.log('Combined data:', data))
.catch(error => console.error('Error in one of the requests:', error));

错误处理最佳实践

async function fetchWithErrorHandling(url, options = {}) {
  try {
    const response = await fetch(url, options);
    
    if (!response.ok) {
      // 尝试解析错误信息
      let errorInfo;
      try {
        errorInfo = await response.json();
      } catch (e) {
        errorInfo = await response.text();
      }
      
      // 根据状态码抛出不同类型的错误
      if (response.status >= 400 && response.status < 500) {
        throw new ClientError(response.status, errorInfo);
      } else if (response.status >= 500) {
        throw new ServerError(response.status, errorInfo);
      }
    }
    
    return await response.json();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      console.log('请求被用户中止');
      throw error;
    }
    
    console.error('请求失败:', error);
    throw error;
  }
}

class ClientError extends Error {
  constructor(status, message) {
    super(`客户端错误: ${status} - ${message}`);
    this.status = status;
  }
}

class ServerError extends Error {
  constructor(status, message) {
    super(`服务器错误: ${status} - ${message}`);
    this.status = status;
  }
}

// 使用示例
fetchWithErrorHandling('https://api.example.com/data')
  .then(data => console.log(data))
  .catch(error => console.error('处理后的错误:', error));

实际应用示例

封装可复用的 HTTP 客户端

class HttpClient {
  constructor(baseURL = '', headers = {}) {
    this.baseURL = baseURL;
    this.headers = {
      'Content-Type': 'application/json',
      ...headers
    };
  }
  
  async get(endpoint, options = {}) {
    return this._fetch(endpoint, { 
      method: 'GET',
      ...options 
    });
  }
  
  async post(endpoint, body, options = {}) {
    return this._fetch(endpoint, {
      method: 'POST',
      body: JSON.stringify(body),
      ...options
    });
  }
  
  async put(endpoint, body, options = {}) {
    return this._fetch(endpoint, {
      method: 'PUT',
      body: JSON.stringify(body),
      ...options
    });
  }
  
  async delete(endpoint, options = {}) {
    return this._fetch(endpoint, {
      method: 'DELETE',
      ...options
    });
  }
  
  async _fetch(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const headers = { ...this.headers, ...options.headers };
    
    const config = {
      ...options,
      headers,
      credentials: 'include' // 默认包含 cookies
    };
    
    const response = await fetch(url, config);
    
    if (!response.ok) {
      const error = new Error(`HTTP错误! 状态码: ${response.status}`);
      error.status = response.status;
      error.response = response;
      throw error;
    }
    
    // 根据响应内容类型决定如何解析
    const contentType = response.headers.get('content-type');
    if (contentType && contentType.includes('application/json')) {
      return response.json();
    }
    return response.text();
  }
}

// 使用示例
const api = new HttpClient('https://api.example.com', {
  'Authorization': 'Bearer your_token_here'
});

// 获取用户
api.get('/users/1')
  .then(user => console.log(user))
  .catch(error => console.error(error));

// 创建新用户
api.post('/users', {
  name: 'John Doe',
  email: 'john@example.com'
}).then(result => console.log(result));

流式数据处理高级用法

Fetch API 的流式处理能力使其特别适合处理大文件或实时数据:

分块读取文本数据

fetch('https://api.example.com/large-text-file.txt')
  .then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let result = '';
    
    function readChunk({ done, value }) {
      if (done) {
        console.log('完整内容:', result);
        return;
      }
      
      result += decoder.decode(value, { stream: true });
      return reader.read().then(readChunk);
    }
    
    return reader.read().then(readChunk);
  });

处理大型 JSON 数据流

async function processLargeJsonStream(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    buffer += decoder.decode(value, { stream: true });
    
    // 处理缓冲区中完整的JSON对象
    let boundary;
    while ((boundary = buffer.indexOf('}\n{')) !== -1) {
      const completeJson = buffer.substring(0, boundary + 1);
      buffer = buffer.substring(boundary + 2);
      
      try {
        const data = JSON.parse(completeJson);
        console.log('处理数据:', data);
      } catch (e) {
        console.error('JSON解析错误:', e);
      }
    }
  }
  
  // 处理剩余数据
  if (buffer) {
    try {
      const data = JSON.parse(buffer);
      console.log('最后的数据:', data);
    } catch (e) {
      console.error('最后JSON解析错误:', e);
    }
  }
}

processLargeJsonStream('https://api.example.com/streaming-data');

总结

Fetch API 相比传统 Ajax 提供了以下优势:

  1. 简洁的语法:基于 Promise 的设计使代码更易读和维护
  2. 灵活的配置:通过配置对象轻松设置请求方法、头部、主体等
  3. 强大的功能:支持流式处理、请求中断、并行请求等高级特性
  4. 多种数据处理方式:支持 JSON、文本、二进制等多种响应格式
  5. 现代标准:是浏览器原生支持的现代 Web 标准

通过上述示例和封装,我们可以看到 Fetch API 如何简化网络请求的处理,使开发者能够更专注于业务逻辑而不是底层实现细节。特别是其流式处理能力,为处理大文件和实时数据提供了优雅的解决方案。

复制全文 生成海报 Web开发 JavaScript API 异步编程 前端技术

推荐文章

Web 端 Office 文件预览工具库
2024-11-18 22:19:16 +0800 CST
全栈工程师的技术栈
2024-11-19 10:13:20 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
Vue 3 中的 Watch 实现及最佳实践
2024-11-18 22:18:40 +0800 CST
Vue3中如何实现国际化(i18n)?
2024-11-19 06:35:21 +0800 CST
Vue 中如何处理跨组件通信?
2024-11-17 15:59:54 +0800 CST
SQL常用优化的技巧
2024-11-18 15:56:06 +0800 CST
gin整合go-assets进行打包模版文件
2024-11-18 09:48:51 +0800 CST
Vue3 结合 Driver.js 实现新手指引
2024-11-18 19:30:14 +0800 CST
如何实现虚拟滚动
2024-11-18 20:50:47 +0800 CST
Vue3中的响应式原理是什么?
2024-11-19 09:43:12 +0800 CST
微信小程序热更新
2024-11-18 15:08:49 +0800 CST
一些高质量的Mac软件资源网站
2024-11-19 08:16:01 +0800 CST
PostgreSQL日常运维命令总结分享
2024-11-18 06:58:22 +0800 CST
如何在Rust中使用UUID?
2024-11-19 06:10:59 +0800 CST
CSS 特效与资源推荐
2024-11-19 00:43:31 +0800 CST
对多个数组或多维数组进行排序
2024-11-17 05:10:28 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
Vue3中如何处理路由和导航?
2024-11-18 16:56:14 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
一些好玩且实用的开源AI工具
2024-11-19 09:31:57 +0800 CST
设置mysql支持emoji表情
2024-11-17 04:59:45 +0800 CST
淘宝npm镜像使用方法
2024-11-18 23:50:48 +0800 CST
使用 Git 制作升级包
2024-11-19 02:19:48 +0800 CST
程序员茄子在线接单