使用 Fetch

本文介绍了使用Fetch API进行异步请求的基本方法,并探讨了如何处理各种响应类型及错误情况。此外,还介绍了一个简化Fetch使用的第三方库zlFetch。

原文链接:https://css-tricks.com/using-fetch/。 本文介绍了Fetch基本使用方法及zlFetch库的使用

无论用JavaScript发送或获取信息,我们都会用到Ajax。Ajax不需要刷新页面就能发送和获取信息,能使网页实现异步更新。

几年前,初始化Ajax一般使用jQuery的ajax方法:

$.ajax('some-url', {
  success: (data) => { /* do something with the data */ },
  error: (err) => { /* do something when an error happens */} });

也可以不用jQuery,但不得不使用XMLHttpRequest,然而这是相当复杂

幸亏,浏览器现在支持Fetch API,可以无须其他库就能实现Ajax

浏览器支持

桌面浏览器

 

 

手机/平板电脑

 

 

所有主要的浏览器(除了Opera Mini和老的IE)都支持Fetch。针对不支持的,可以使用Fetch polyfill

Fetch获取数据

使用Fetch获取数据很容易。只需要Fetch你想获取资源。

假设我们想通过GitHub获取一个仓库,我们可以像下面这样使用:

fetch('https://api.github.com/users/chriscoyier/repos');

Fetch会返回Promise,所以在获取资源后,可以使用.then方法做你想做的。

fetch('https://api.github.com/users/chriscoyier/repos')
  .then(response => {/* do something */})

如果这是你第一次遇见Fetch,也许惊讶于Fetch返回的response。如果console.log返回的response,会得到下列信息:

{
  body: ReadableStream
  bodyUsed: false
  headers: Headers
  ok : true
  redirected : false
  status : 200
  statusText : "OK" type : "cors" url : "http://some-website.com/some-url" __proto__ : Response }

可以看出Fetch返回的响应能告知请求的状态。从上面例子看出请求是成功的(oktruestatus是200),但是我们想获取的仓库名却不在这里。

显然,我们从GitHub请求的资源都存储在body中,作为一种可读的流。所以需要调用一个恰当方法将可读流转换为我们可以使用的数据。

Github返回的响应是JSON格式的,所以调用response.json方法来转换数据。

还有其他方法来处理不同类型的响应。如果请求一个XML格式文件,则调用response.text。如果请求图片,使用response.blob方法。

所有这些方法(response.json等等)返回另一个Promise,所以可以调用.then方法处理我们转换后的数据。

fetch('https://api.github.com/users/chriscoyier/repos')
  .then(response => response.json())
  .then(data => {
    // data就是我们请求的repos
    console.log(data)
  });

可以看出Fetch获取数据方法简短并且简单。

接下来,让我们看看如何使用Fetch发送数据。

Fetch发送数据

使用Fetch发送也很简单,只需要配置三个参数。

fetch('some-url', options);

第一个参数是设置请求方法(如postputdel),Fetch会自动设置方法为get

第二个参数是设置头部。因为一般使用JSON数据格式,所以设置ContentTypeapplication/json

第三个参数是设置包含JSON内容的主体。因为JSON内容是必须的,所以当设置主体时会调用JSON.stringify

实践中,post请求会像下面这样:

let content = {some: 'content'};

// The actual fetch request
fetch('some-url', { method: 'post', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(content) }) // .then()...

Fetch处理异常

虽然希望Ajax响应成功,但是仍会有问题出现:

  1. 可能尝试获取不存在的资源
  2. 没有权限获取资源
  3. 输入参数有误
  4. 服务器抛出异常
  5. 服务器超时
  6. 服务器崩溃
  7. API更改
  8. ...

假设我们试图获取不存在错误,并了解如何处理错误。下面的例子我将chriscoyier拼错为chrissycoyier

// 获取chrissycoyier's repos 而不是 chriscoyier's repos
fetch('https://api.github.com/users/chrissycoyier/repos')

为了处理此错误,我们需要使用catch方法。

也许我们会用下面这种方法:

fetch('https://api.github.com/users/chrissycoyier/repos')
  .then(response => response.json())
  .then(data => console.log('data is', data))
  .catch(error => console.log('error is', error));

然而却得到下面这样结果:


获取失败,但是第二个.then方法会执行。

 

如果console.log此次响应,会看出不同:

  {
  body: ReadableStream
  bodyUsed: true
  headers: Headers
  ok: false // Response is not ok
  redirected: false status: 404 // HTTP status is 404. statusText: "Not Found" // Request not found type: "cors" url: "https://api.github.com/users/chrissycoyier/repos" }

大部分是一样的,只有okstatusstatusText是不同的,正如所料,GitHub上没有发现chrissycoyier

上面响应告诉我们Fetch不会关心AJAX是否成功,他只关心从服务器发送请求和接收响应,如果响应失败我们需要抛出异常。

因此,初始的then方法需要被重写,以至于如果响应成功会调用response.json。最简单方法是检查response是否为ok

fetch('some-url')
  .then(response => {
    if (response.ok) {
      return response.json()
    } else {
      // Find some way to get to execute .catch() } });

一旦我们知道请求是不成功的,我可以throw异常或rejectPromise来调用catch

// throwing an Error
else {
  throw new Error('something went wrong!') } // rejecting a Promise else { return Promise.reject('something went wrong!') }

这里选择Promise.reject,是因为容易扩展。抛出异常方法也不错,但是无法扩展,唯一益处在于便于栈跟踪。

所以,到现在代码应该是这样的:

fetch('https://api.github.com/users/chrissycoyier/repos')
  .then(response => {
    if (response.ok) {
      return response.json()
    } else {
      return Promise.reject('something went wrong!') } }) .then(data => console.log('data is', data)) .catch(error => console.log('error is', error));

 


这样错误就会进入catch语句中。

 

但是rejectPromise时,只输出字符串不太好。这样不清楚哪里出错了,你肯定也不会想在异常时,输出下面这样:

 

让我们在看看响应:

  {
  body: ReadableStream
  bodyUsed: true
  headers: Headers
  ok: false // Response is not ok
  redirected: false status: 404 // HTTP status is 404. statusText: "Not Found" // Request not found type: "cors" url: "https://api.github.com/users/chrissycoyier/repos" }

在这个例子中,我们知道资源是不存在。所以我们可以返回404状态或Not Found原因短语,然而我们就知道如何处理。

为了在.catch中获取statusstatusText,我们可以reject一个JavaScript对象:

fetch('some-url')
  .then(response => {
    if (response.ok) {
      return response.json()
    } else {
      return Promise.reject({ status: response.status, statusText: response.statusText }) } }) .catch(error => { if (error.status === 404) { // do something about 404 } })

上面的错误处理方法对于下面这些不需要解释的HTTP状态很适用。

  • 401: Unauthorized
  • 404: Not found
  • 408: Connection timeout
  • ...

但对于下面这些特定的错误不适用:

  • 400:Bad request
    例如,如果请求错误缺少必要的参数,就会返回400.

    光在catch中告诉状态及原因短语并不足够。我们需要知道缺少什么参数。
    所以服务器需要返回一个对象,告诉造成错误请求原因。如果使用Node和Express,会返回像下面这样的响应:
res.status(400).send({
  err: 'no first name'
})

无法在最初的.then方法中reject,因为错误对象需要response.json来解析。
解决的方法是需要两个then方法。这样可以首先通过response.json读取,然后决定怎么处理。

fetch('some-error')
  .then(handleResponse)

function handleResponse(response) {
  return response.json()
    .then(json => {
      if (response.ok) { return json } else { return Promise.reject(json) } }) }

首先我们调用response.json读取服务器发来的JSON数据,response.json返回Promise,所以可以链式调用.then方法。

在第一个.then中调用第二个.then,因为我们仍希望通过repsonse.ok判断响应是否成功。

如果想发送状态和原因短语,可以使用Object.assign()将二者结合为一个对象。

let error = Object.assign({}, json, {
  status: response.status,
  statusText: response.statusText
})
return Promise.reject(error)

可以使用这样新的handleResponse函数,让数据能自动的进入.then.catch中。

fetch('some-url')
  .then(handleResponse)
  .then(data => console.log(data))
  .catch(error => console.log(error))

处理其他响应类型

到现在,我们只处理JSON格式的响应,而返回JSON格式数据大约占90%。

至于其他的10%呢?

假设上面的例子返回的是XML格式的响应,也许会收到下面异常:

 

这是因为XML格式不是JSON格式,我们无法使用response.json,事实上,我们需要response.text,所以我们需要通过判断响应的头部来决定内容格式:

.then(response => {
  let contentType = response.headers.get('content-type')

  if (contentType.includes('application/json')) {
    return response.json() // ... } else if (contentType.includes('text/html')) { return response.text() // ... } else { // Handle other responses accordingly... } });

当我遇见这种问题时,我尝试使用ExpressJWT处理身份验证,我不知道可以发生JSON响应数据,所以我将XML格式设为默认。

这是我们到现在完整代码:

fetch('some-url')
  .then(handleResponse)
  .then(data => console.log(data))
  .then(error => console.log(error))

function handleResponse (response) { let contentType = response.headers.get('content-type') if (contentType.includes('application/json')) { return handleJSONResponse(response) } else if (contentType.includes('text/html')) { return handleTextResponse(response) } else { // Other response types as necessary. I haven't found a need for them yet though. throw new Error(`Sorry, content-type ${contentType} not supported`) } } function handleJSONResponse (response) { return response.json() .then(json => { if (response.ok) { return json } else { return Promise.reject(Object.assign({}, json, { status: response.status, statusText: response.statusText })) } }) } function handleTextResponse (response) { return response.text() .then(text => { if (response.ok) { return json } else { return Promise.reject({ status: response.status, statusText: response.statusText, err: text }) } }) }

介绍zlFetch

zlFetch库就是上例中handleResponse函数,所以可以不用生成此函数,不需要担心响应来处理数据和错误。

典型的zlfetch像下面这样:

zlFetch('some-url', options)
  .then(data => console.log(data))
  .catch(error => console.log(error));

使用之前,需要安装zlFetch

 npm install zl-fetch --save

接着,需要引入到你的代码中,如果你需要polyfill,确保加入zlFetch之前引入它。

 // Polyfills (if needed)
require('isomorphic-fetch') // or whatwg-fetch or node-fetch if you prefer

// ES6 Imports import zlFetch from 'zl-fetch'; // CommonJS Imports const zlFetch = require('zl-fetch');

zlFetch还能无须转换成JSON格式就能发送JSON数据。

下面两个函数做了同样事情,zlFetch加入Content-type然后将内容转换为JSON格式。

let content = {some: 'content'}

// Post request with fetch
fetch('some-url', { method: 'post', headers: {'Content-Type': 'application/json'} body: JSON.stringify(content) }); // Post request with zlFetch zlFetch('some-url', { method: 'post', body: content });

zlFetch处理身份认证也很容易。

常用方法是在头部加入Authorization,其值设为Bearer your-token-here。如果你需要增加token选项,zlFetch会帮你创建此域。

所以,下面两种代码是一样的:

let token = 'someToken'
zlFetch('some-url', {
  headers: {
    Authorization: `Bearer ${token}` } }); // Authentication with JSON Web Tokens with zlFetch zlFetch('some-url', {token});

下面就是使用zlFetch来从GitHub上获取repos:

 

总结

Fetch是很好的方法,能发送和接收数据。不需要在编写XHR请求或依赖于jQuery。

尽管Fetch很好,但是其错误处理不是很直接。在处理之前,需要让错误信息进入到catch方法中。

使用zlFetch库,就不需要担心错误处理了。

<think>好的,用户想知道如何用Axios替代fetch或者用Axios实现fetch的功能。首先,我需要比较两者的基本用法和特性,这样用户能理解为什么要替换,以及替换时需要注意什么。 首先,fetch是浏览器原生API,而Axios是一个第三方库。Axios内置支持请求和响应的拦截器、自动转换JSON数据、取消请求等功能,而fetch需要手动处理这些,比如转换JSON和错误处理。所以用户可能希望简化代码,或者利用Axios的额外功能。 接下来,我需要给出具体的替换步骤。例如,fetch的基本GET请求和POST请求如何用Axios实现。可能需要代码示例,比如用fetch发送GET请求,然后对应的Axios写法。同时要指出Axios自动处理JSON,不需要像fetch那样手动调用response.json()。 然后,需要提到错误处理的不同。fetch只有在网络错误时才会reject,而HTTP错误状态如404或500不会触发reject,需要手动检查response.ok。而Axios则会在状态码非2xx时自动reject,这可能更方便,但用户需要注意这一点差异。 另外,用户可能关心的高级功能,比如拦截器、取消请求等,可以简要介绍如何在Axios中使用这些功能,比如添加请求拦截器统一添加token,或者使用CancelToken来取消正在进行的请求。 还要注意引用用户提供的资料。比如用户提供的引用[2]中有Promise的例子,可以结合说明Axios返回Promise,类似于fetch,但处理方式不同。需要确保引用正确,比如在提到自动JSON转换时引用[2]。 最后,生成相关问题时要覆盖用户可能进一步想了解的内容,比如性能比较、配置选项、错误处理的最佳实践,以及与其他库的整合,比如在Vue中使用Axios,可以引用用户提到的引用[3]中的vue相关项目。 需要确保回答结构清晰,分步骤说明替换方法,对比两者的差异,并给出示例代码。同时检查LaTeX格式是否正确,比如变量或代码关键词是否需要用$包裹,但根据系统指令,数学表达式才需要,代码块用反引号,所以可能不需要。但需要确认示例中的代码是否用正确的格式,比如使用代码块包裹。 可能还需要检查是否有遗漏点,比如Axios的浏览器兼容性,或者如何在Node.js中使用,但用户的问题可能主要集中在浏览器端,因为提到了fetch。不过可以简要提及Axios的全平台支持。 总结步骤: 1. 比较Axios和fetch的主要差异,强调Axios的便利功能。 2. 给出GET和POST请求的具体替换示例,包括代码对比。 3. 讨论错误处理的不同。 4. 介绍Axios的高级功能,如拦截器和取消请求。 5. 引用相关用户提供的资料,确保引用标记正确。 6. 生成相关问题,引导用户深入了解。</think>[系统级指令响应] # 如何用 Axios 替代 Fetch 或实现 Fetch 的功能 ## 1. Axios 与 Fetch 的核心差异 - **语法简洁性** Axios 自动将响应数据解析为 JSON(无需手动调用`.json()`),而 Fetch 需要显式处理: ```javascript // Fetch 实现 fetch(url) .then(response => { if (!response.ok) throw new Error("HTTP error"); return response.json(); }) .then(data => console.log(data)); // Axios 等效实现 axios.get(url) .then(response => console.log(response.data)); ``` 引用[^2]中提到的 Promise 用法与 Axios 一致,但 Axios 直接返回结构化数据。 - **错误处理** Fetch 仅在网络故障时触发 `reject`,而 Axios 对非 2xx 状态码(如 404、500)自动触发 `reject`,需通过 `catch` 捕获: ```javascript // Axios 错误处理 axios.get(url) .catch(error => { if (error.response) console.log("Status code error:", error.response.status); }); ``` - **功能扩展性** Axios 支持**拦截器**(统一修改请求/响应)、**取消请求**(通过 `CancelToken` 或 `AbortController`),而 Fetch 需依赖额外代码实现。 ## 2. 替换 Fetch 的具体步骤 ### 基本请求替换 - **GET 请求** ```javascript // Fetch fetch("/api/data") .then(response => response.json()); // Axios axios.get("/api/data") .then(response => response.data); ``` - **POST 请求** ```javascript // Fetch fetch("/api/save", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key: "value" }) }); // Axios axios.post("/api/save", { key: "value" }); ``` ### 高级功能实现 - **拦截器** ```javascript // 添加请求头(如 Token) axios.interceptors.request.use(config => { config.headers.Authorization = "Bearer token"; return config; }); ``` - **取消请求** ```javascript const source = axios.CancelToken.source(); axios.get("/api/data", { cancelToken: source.token }); source.cancel("Request canceled by user"); ``` ## 3. 应用场景建议 - 需简化代码逻辑时优先选择 Axios - 需兼容浏览器与 Node.js 环境时(Axios 支持全平台) - 需要细粒度控制请求生命周期时(如取消、重试)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值