2024-12-12    2024-12-12    2467 字  5 分钟

fetch 是现代 JavaScript 中用于发起网络请求的一个全局 API。它提供了一种简单、灵活的方式来与服务器进行通信,基于 Promise,因此非常适合处理异步操作。fetchXMLHttpRequest 的替代品,广泛用于前端开发中。

基础

基本用法

fetch 的基本语法如下:

1
2
3
4
5
6
7
fetch(resource, options)
  .then(response => {
    // 处理响应
  })
  .catch(error => {
    // 处理错误
  });
  • resource: 请求的资源,通常是一个 URL。
  • options: 可选的配置对象,用于指定请求的方法、头信息、请求体等。

示例:GET 请求

1
2
3
4
5
6
7
8
fetch('https://jsonplaceholder.typicode.com/posts')
  .then(response => response.json()) // 将响应转换为 JSON
  .then(data => {
    console.log(data); // 输出: 包含所有帖子的数组
  })
  .catch(error => {
    console.error('Error:', error);
  });

示例:POST 请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST', // 请求方法
  headers: {
    'Content-Type': 'application/json' // 请求头
  },
  body: JSON.stringify({
    title: 'foo',
    body: 'bar',
    userId: 1
  }) // 请求体
})
  .then(response => response.json())
  .then(data => {
    console.log(data); // 输出: 新创建的帖子对象
  })
  .catch(error => {
    console.error('Error:', error);
  });

处理响应

fetch 返回的 response 对象包含多种方法来处理不同类型的响应数据:

  • response.json(): 将响应体解析为 JSON 格式。
  • response.text(): 将响应体解析为文本格式。
  • response.blob(): 将响应体解析为 Blob 对象。
  • response.arrayBuffer(): 将响应体解析为 ArrayBuffer 对象。
1
2
3
4
5
6
7
8
fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json())
  .then(data => {
    console.log(data); // 输出: 单个帖子的对象
  })
  .catch(error => {
    console.error('Error:', error);
  });

处理错误

fetch 只有在网络错误(如无法连接到服务器)时才会进入 catch 块。如果服务器返回一个错误状态码(如 404 或 500),fetch 不会自动抛出错误,你需要手动检查 response.okresponse.status

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fetch('https://jsonplaceholder.typicode.com/nonexistent')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

使用 async/await

fetchasync/await 结合使用可以使代码更加简洁和易读。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
async function fetchPosts() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

fetchPosts();

配置选项

fetchoptions 参数可以包含以下常用选项:

  • method: 请求方法(如 GET, POST, PUT, DELETE)。
  • headers: 请求头(如 Content-Type, Authorization)。
  • body: 请求体(通常用于 POSTPUT 请求)。
  • mode: 请求模式(如 cors, no-cors, same-origin)。
  • credentials: 是否发送 cookies(如 omit, same-origin, include)。
  • cache: 缓存模式(如 default, no-store, reload, no-cache, force-cache)。
  • redirect: 重定向模式(如 follow, error, manual)。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'foo',
    body: 'bar',
    userId: 1
  }),
  mode: 'cors',
  credentials: 'same-origin'
})
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

总结

fetch 是一个强大且灵活的 API,用于发起网络请求。它基于 Promise,因此非常适合处理异步操作。通过 fetch,你可以轻松地与服务器进行通信,处理各种类型的响应数据,并结合 async/await 使代码更加简洁和易读。

附录

fetch 相较于传统的 XHR 的优势和不足

优势的方面

1、基于 Promise

  • 优势: fetch 基于 Promise,使得异步代码的编写更加简洁和直观。可以使用 then()catch() 链式调用,或者结合 async/await 编写更易读的代码。
    1
    2
    3
    4
    
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => console.error(error));
    
  • 对比 XHR: XHR 使用回调函数处理异步操作,容易导致“回调地狱”,代码可读性较差。

2、更简洁的 API

  • 优势: fetch 的 API 设计更加简洁,语法更现代化。只需调用 fetch(url, options),而不需要像 XHR 那样手动设置 open()send() 和事件监听器。
    1
    2
    3
    4
    5
    
    fetch('https://api.example.com/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ key: 'value' })
    });
    
  • 对比 XHR: XHR 的 API 较为冗长,需要手动设置请求方法、头信息和事件监听器。

3、内置支持多种数据格式

  • 优势: fetch 提供了内置方法来处理不同类型的响应数据(如 response.json()response.text()response.blob() 等),使得处理响应更加方便。
    1
    2
    3
    
    fetch('https://api.example.com/data')
      .then(response => response.json()) // 自动解析 JSON
      .then(data => console.log(data));
    
  • 对比 XHR: XHR 需要手动解析响应数据(如 JSON.parse(xhr.responseText))。

4、更现代的错误处理

  • 优势: fetch 的错误处理更加现代化。只有网络错误(如无法连接到服务器)才会触发 catch 块,而 HTTP 错误(如 404 或 500)不会自动抛出错误,需要手动检查 response.okresponse.status
    1
    2
    3
    4
    5
    6
    7
    8
    
    fetch('https://api.example.com/data')
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .catch(error => console.error(error));
    
  • 对比 XHR: XHR 的错误处理依赖于 status 属性,需要手动检查状态码。

5、支持跨域请求(CORS)

  • 优势: fetch 默认支持跨域请求(CORS),并且可以通过 mode 选项(如 corsno-corssame-origin)灵活控制请求的行为。
    1
    2
    3
    
    fetch('https://api.example.com/data', {
      mode: 'cors'
    });
    
  • 对比 XHR: XHR 也支持 CORS,但配置相对复杂。

6、支持流式数据

  • 优势: fetch 支持流式数据处理,可以逐步读取大文件或流式响应。
    1
    2
    3
    4
    5
    
    fetch('https://api.example.com/large-file')
      .then(response => response.body.getReader())
      .then(reader => {
        // 逐步读取数据
      });
    
  • 对比 XHR: XHR 不支持流式数据处理。

不足的方面

1、不支持进度事件

  • 不足: fetch 不支持进度事件(如 xhr.upload.onprogressxhr.onprogress),因此无法实时监控文件上传或下载的进度。
    1
    2
    3
    4
    
    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = function (event) {
      console.log(`Uploaded ${event.loaded} of ${event.total} bytes`);
    };
    
  • 对比 XHR: XHR 提供了 onprogress 事件,可以方便地监控上传或下载的进度。

2、默认不发送 cookies

  • 不足: fetch 默认不会发送跨域请求的 cookies(除非显式设置 credentials: 'include')。
    1
    2
    3
    
    fetch(' https://api.example.com/data ', {
      credentials: 'include' // 需要显式开启
    });
    
  • 对比 XHR: XHR 默认会发送 cookies,无需额外配置。

3、不支持超时设置

  • 不足: fetch 没有内置的超时机制,需要手动实现超时逻辑。
    1
    2
    3
    4
    5
    6
    7
    
    const controller = new AbortController();
    const signal = controller.signal;
    
    setTimeout(() => controller.abort(), 5000); // 5 秒超时
    
    fetch(' https://api.example.com/data ', { signal })
      .catch(error => console.error('Request timed out'));
    
  • 对比 XHR: XHR 提供了 timeout 属性,可以轻松设置请求超时。

4、兼容性问题

  • 不足: fetch 在某些旧版浏览器(如 IE11)中不被支持,需要使用 polyfill 来兼容。
    1
    2
    
    <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>
    
  • 对比 XHR: XHR 在所有现代浏览器和旧版浏览器中都得到了广泛支持。

5、复杂场景下功能不足

  • 不足: 在某些复杂场景下(如需要精细控制请求行为或处理二进制数据),fetch 的功能可能不如 XHR 灵活。
  • 对比 XHR: XHR 提供了更底层、更精细的控制能力。

总结

特性 fetch 优势 fetch 不足
API 设计 基于 Promise,语法简洁,支持链式调用和 async/await 不支持进度事件,无法实时监控上传或下载进度。
错误处理 现代化错误处理,结合 Promise 更易读。 默认不发送 cookies,需要显式配置。
数据处理 内置支持多种数据格式(如 JSON、文本、Blob)。 不支持超时设置,需要手动实现。
兼容性 现代浏览器支持良好,但需要 polyfill 兼容旧版浏览器。 在旧版浏览器(如 IE11)中不被支持。
功能灵活性 支持流式数据处理,适合处理大文件或流式响应。 在复杂场景下功能不如 XHR 灵活。

通过以上对比可以看出,fetch 是一个更现代化、更简洁的 API,适合大多数场景。但在需要精细控制请求行为或兼容旧版浏览器的场景下,XHR 仍然是一个可靠的选择。