您所在的位置:首页 / 知识分享

前端fetch初探

2020.08.06

1514

fetch是一种使用promise为构建块的现代异步网络请求方法.是当今进行异步网络请求的新标准.

fetch是什么?

fetch是一种使用promise为构建块的现代异步网络请求方法.是当今进行异步网络请求的新标准.除了IE之外,在各大浏览器中的兼容性都还可以,在caniuse上查询fetch的浏览器兼容性,不支持的浏览器可以使用 fetch polyfill.其本质是一种标准,该标准定义了请求,响应和绑定的流程,还定义了Fetch的JavaScript API.而Fetch API 提供了fetch()方法.它被定义在BOM的window对象中,返回一个Promise对象,因此我们能够对返回的结果进行检索.

fetch怎么用?

为了方便测试,我们选择自己用express来写一个简单的接口.当然大家也可以使用在线接口来测试fetch的功能.这个在线接口地址的例子就是用fetch来写的.

get请求

下面是接口代码,先是简单的定义了一个get请求,返回一个字符串.

const express = require('express') const app = new express() // 设置接口允许跨域 app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin','*')
  next()
}) // 编写一个get请求 app.get('/test', (req, res, next) => {
  res.send('Hello world')
}) const port = process.env.port || 8686 app.listen(port, () => { console.log('Server start in http://localhost:%s', port)
}) 复制代码

fetch()的第一个参数是要获取资源的url,第二个参数可选,是个配置选项.

fetch('http://localhost:8686/test').then(response => response.text()).then(res => { console.log(res) // Hello world }).catch(error => { console.log(error)
}) 复制代码

fetch方法的then会接收一个Response实例.但值得注意的是,fetch方面的第二个then接收的才是后台传过来的真正数据,而前一个then则是对数据进行处理或者异常捕获.在上面的例子中,response => response.text()就是在第一个then里面对数据进行处理,返回一个处理后的数据.这里使用的是text()方法,它可以将主体内容作为字符串返回.其他的方法还有json(),blob(),formData和arrayBuffer(). 我们再来看看json()的用处,它可以将数据反序列化为一个对象. 在接口代码中,添加如下:

app.get('/test2', (req, res, next) => {
  res.json({ name:'zhangsan', age:12 })
}) 复制代码

若结果还是用text()返回的话,则是{"name":"zhangsan","age":12}字符串,但是若用json()返回,就是一个对象了{name: "zhangsan", age: 12}.很明显,在这里使用json()就比text()要好.

第一个then里面的处理方法总结

  • text() 返回一个被解析为USVString格式的Promise对象
  • json() 返回一个被解析为JSON格式的Promise对象
  • blob() 返回一个被解析为Blob格式的Promise对象
  • formData() 返回一个被解析为FormData格式的Promise对象
  • arrayBuffer() 返回一个被解析为ArrayBuffer格式的Promise对象

使用async改写

上面所有的方法都会返回Promsie,所以我们可以在后面继续使用一个then和一个catch.当然了,我们也可以使用async和await来改写上面的代码.

!(async () => { const response = await fetch('http://localhost:8686/test2') const res = await response.json() console.log(res) // {name: "zhangsan", age: 12} })() 复制代码

错误捕获

前面提到了,可以在第一个then里面对数据进行处理,也可以捕获异常.我们将请求的url改为一个不存在的地址.

fetch('http://localhost:8686/test666').then(response => { if(!response.ok){ throw new Error(response.statusText) // throw an Error // return Promise.reject({  // rejecting a Promise //   status:response.status, //   statusText:response.statusText // }) } return response.json()
}).then(res => { console.log(res)
}).catch(error => { console.log(error) // Error: Not Found }) 复制代码

上面的代码将会抛出一个异常,我们也可以通过Promise的reject来调用catch.

post请求

上面差不多把一个最简单的get请求讲完了,接下来我们继续说一说post请求要怎么发送.我们先修改服务端的代码如下,新增加了一个post请求的接口.这个接口将请求时发送的数据外加一个表示结果的字段一起打包返回回去.

const express = require('express') const router = express.Router() const bodyParser = require('body-parser') const app = new express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json()) // 设置接口允许跨域 app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin','*')
  res.header('Access-Control-Allow-Headers', 'Content-Type')
  next()
}) // 编写一个get请求 app.get('/test', (req, res, next) => {
  res.send('Hello world')
})

app.get('/test2', (req, res, next) => { console.log(req)
  res.json({ name:'zhangsan', age:12 })
})

app.post('/create', (req, res, next) => { let obj = Object.assign({result:'success'}, req.body)
  res.status(200).json(obj)
}) const port = process.env.port || 8686 app.listen(port, () => { console.log('Server start in http://localhost:%s', port)
}) 复制代码

前端发送fetch请求的代码如下:

const data = { name:'zhangsan', age:12 }

fetch('http://localhost:8686/create',{ method:'post', body:JSON.stringify(data), // 请求的body信息,get和head方法是不能包含body信息 headers:{ // 配置请求的头信息,包含与请求关联的Headers对象 'Content-Type':'application/json;charset=utf-8' }
}).then(response => { return response.json()
}).then(res => { console.log(res) // {result: "success", name: "zhangsan", age: 12} }).catch(error => { console.log(error)
}) 复制代码

第二个参数的配置项

在上面的代码中,我们使用了fetch方法的第二个参数,即配置选项.其中,method表示请求方式,默认为get, 其他的还有post,put,head,delete等.body为请求的body信息,get和head是没有body的.而headers则是请求头相关信息.在这个配置选项中,还有一些配置项目,我们来简单的说一下.

  • credentials 要让浏览器发送包含凭证(cookie)的请求(即使是跨域源),就要将此属性设置为include,same-origin则是请求url和调用脚本位于同源时发送凭证,若是不想发送凭证则是omit.这里就表现出了fetch和ajax的一个区别:默认情况下,fetch不会接受或者发送cookies.
  • mode 设置请求模式,属性值有crosno-corssame-origin.其中same-origin表示必须同源,禁止跨域,否则会报Request mode is "same-origin" but the URL's origin is not same as the request origin xxx的错误.若设置了cros则需要服务端配合设置响应头Access-Control-Allow-Origin为相应的域名或*.no-cors的效果就是无论外域服务器是否设置了允许跨域的响应头,浏览器都 可以对外域发送请求,但是它不接受响应.
  • cache 设置请求的缓存模式,属性值有defaultreloadno-cacheno-storeforce-cacheonly-if-cached
  • referrer 来源地址no-referrerclient.注意了,这里采用的referrer这种正确的拼法,而不是referer这种错误的拼写.有关referrer的拼法的历史趣闻,可看我文末的第二个参考链接.

Request构造器函数

除了上面介绍的fetch的使用方法外,我们还可以通过一个Request构造器函数来创建一个新的请求对象.

let req = new Request('http://localhost:8686/test',{ method:'get' })
fetch(req).then(response => response.text()).then(res => { console.log(res) // Hello world }).catch(err => { console.log(err)
}) 复制代码

Headers构造器函数

我们也可以通过一个Headers构造器函数来设置我们的请求头

myHeader = new Headers()
myHeader.append('Content-Type','application/json;charset=utf-8')
... 
{ headers:myHeader
} 复制代码

fetch与ajax有什么关系?

fetch是XMLHttpRequest的一种替代方案,fetct就是原生的js,不是ajax的进一步封装.

区别

  • fetch返回的promise不会拒绝http的错误状态,即使响应是404或者500,它们不被认为是网络错误.只有当网络故障或者请求被阻止时,才会标记为reject.因此成功的fetch()检查不仅要包含promise被resolve,还要包括response.ok属性为true,该属性是检查response的状态是否在200-299这个范围内来确定的.
  • 默认情况下,fetch不会接受或者发送cookies

fetch的优势

  • fetch请求相对来说语法简洁,代码更少,更具语义化,且数据处理过程更加清晰
  • 基于标准的Promise实现,且支持async/await,避免了回调地狱,
  • 接口更加的合理化,因为ajax是将所有不同性质的接口都放在了XHR对象身上,而fetch则是分散在不同的对象上,如 Headers, Response, Request等
  • 可以在ServiceWorker中使用

fetch会出现reject的情况

上面说到了,只有当网络故障或者请求被阻止的时候,才会标记为reject.下面我们来看下这两种情况.

网络故障

我们去Chrome浏览器中,将Network中的网络设置为Offline离线状态,再次调用如下代码,就会发现控制台打印出错误.请求结果进入到reject状态了.

fetch('http://localhost:8686/test').then(response => response.text()).then(res => { console.log(res)  
}).catch(error => { console.log(error) // TypeError: Failed to fetch }) 复制代码

请求中止

fetch本身并没有提供中止请求的方法.但是部分浏览器有实现AbortController,可以用来中止fetch请求.

const controller = new AbortController() const signal = controller.signal setTimeout(() => { controller.abort() }, 200) // 自己调整定时器的时长查看效果,可以配合Chrome浏览器中的Network,将网络调整为慢速3G fetch('http://localhost:8686/test',{
  signal // 将上面的signal加入到配置项中 }).then(response => response.text()).then(res => { console.log(res)  
}).catch(error => { console.log(error) // DOMException: The user aborted a request. }) 复制代码

总结


fetch

相关新闻

www.apple.com,你敢肯定它就是苹果官网么?

2018.09.16

3782

大部分人在浏览网站时,都会用肉眼来观察网站的 URL 地址,以及地址旁边的安全标识来判断网站是否是钓鱼网站。 现在这种方法完全失效了!只要攻击者做出一个类似文章开头那样的淘宝或者京东之类的购物网站,甚至是银行官网,用户根本无从辨别。

用3个章节,聊聊SaaS用户体验设计实践与价值

2023.03.07

235

今天和大家聊聊我对「SaaS」和「SaaS 用户体验」价值思考及设计实践总结分享,本文从如下三个方面展开: 如何理解 SaaS 和传统软件的差别 重新认识 B 端用户体验的价值 SaaS 用户体验设计体系实践思考。