jQuery 当初的流行带来了一个很重要的 API 那就是 $.ajax() 相信很多同学都深爱过它,究其原因是原生 XMLHttpRequest 的不友好,只能借助第三方框架来发送 Ajax 请求,近来也出现了更多好用的框架,比如 axios,难道真的就只能借助第三方框架来做这个事吗?
还真不是,新推出的 Fetch API 就是用来替代 XMLHttpRequest 的,功能类似,更具可扩展性和高效性。我们来看一个简单的例子
fetch('https://api.awesomes.cn/repo/top100') .then(function(response) { return response.json() }) .then(function(data) { console.log(data) })
注意,这和使用 axios 是不一样的,并非一次到位就能拿到响应 body,而是要经过两次 Promise.then(),当然我们可以很容易地将其变成 async await 的方式
let fetchData = async () => { let response = await fetch('https://api.awesomes.cn/repo/top100') let data = await response.json() }
fetch() 方法返回的是一个 Response 对象,结构如下
{ body: ReadableStream, bodyUsed: false, headers: Headers, ok: true redirected: false status: 200 statusText: "OK" type: "cors", url: "https://api.awesomes.cn/repo/top100" }
这里的响应正文 body 实际上是一个流,很明显是不能直接使用的,只能通过下面的方法来获取不同格式的内容
- Body.arrayBuffer() 使用 buffer 数组来读取
- Body.blob() 使用 Blob 对象来读取
- Body.formData() 使用 FormData 对来读取
- Body.json() 使用 JSON 对来读取
- Body.text() 使用文本对来读取
比如下载一张图片并用 <img> 标签显示(注意跨域)
<img id="imgbox"> .... let setImg = async () => { let response = await fetch('https://avatars1.githubusercontent.com/in/2740') let data = await response.blob() let objectURL = URL.createObjectURL(data) let myImage = document.querySelector('#imgbox') myImage.src = objectURL }
不管使用上面哪种读取方式,body 只能被读取一次,然后 bodyUsed 的值都会被修改成 true,表示已被使用,之后就不能再读取了。
let fetchData = async () => { let response = await fetch('https://api.awesomes.cn/repo/top100') console.log('bodyUsed', response.bodyUsed) let data = await response.json() console.log('bodyUsed', response.bodyUsed) let dataAgain = await response.json() console.log('dataAgain', dataAgain) } fetchData() // bodyUsed false // bodyUsed true // Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream is locked
可以看到,试图再次读取 body 是会报错的,如果我就是想要多次读取呢?我们可以使用 clone() 方法将 Response对象克隆一个出来
let fetchData = async () => { let response = await fetch('https://api.awesomes.cn/repo/top100') let clone1 = response.clone() let clone2 = response.clone() let data = await response.json() let data1 = await clone1.json() let data2 = await clone2.json() console.log(data, data1, data2) // [data] [data] [data] }
注意,每个克隆的对象也只能用一次,要实现多次读取就只能是克隆多个出来。
fetch() 不是只能发送 GET 请求,所有方法的请求都能发送,这取决于它的第二个参数,我们来看看 fetch 的实际语法
fetch(input[, init])
- input 一个 URL 或者 Request 对象
- init 可选参数,请求配置(method:请求方法,headers 请求头 body请求体...)
比如要发送一个 POST 请求
let submit = async () => { await fetch(url, { method: 'POST', body: JSON.stringify(data) }) }
当然也可以用 Request 构造一个对象传入 fetch
let submit = async () => { let _request = new Request(url, { method: 'POST', body: JSON.stringify(data) } await fetch(_request) }
要让请求携带请求头需要用 Headers() 构造函数来创建一个自己的 headers 对象
let submit = async () => { let headers = new Headers({ 'Content-Type': 'application/json' }) headers.append('token', 'xxx') fetch(url, { method: 'POST', body: JSON.stringify(data), headers: headers }) }
如果我要发送一个表单数据呢?可以通过 FormData() 构造函数基于 form 标签构建一个对象出来
var form = new FormData(document.getElementById('login-form')) fetch("/login", { method: "POST", body: form })
除了 IE(非 Edge)目前主流的浏览器都支持 fetch,如果你有更高的兼容性需求,也可以加上 fetch 的 polyfil:https://github.com/github/fetch