etch 是 XMLHttpRequest 的升级版,使用js脚本发出网络请求,但是与 XMLHttpRequest 不同的是,fetch 方式使用 Promise,相比 XMLHttpRequest 更加简洁。所以我们告别XMLHttpRequest,引入 fetch 如何使用?
fetch() 是一个全局方法,提供一种简单,合理的方式跨网络获取资源。它的请求是基于 Promise 的,需要详细学习 Promise ,请点击《 Promise详解 》。它是专门为了取代传统的 xhr 而生的。
1.1、fetch使用语法
fetch(url,options).then((response)=>{
//处理http响应
},(error)=>{
//处理错误
})
url :是发送网络请求的地址。
options:发送请求参数,
1.2、response 对象
fetch 请求成功后,响应 response 对象如图:
1.3、读取内容方法
response 对象根据服务器返回的不同类型数据,提供了不同的读取方法。分别有:
上述 5 个方法,返回的都是 promise 对象,必须等到异步操作结束,才能得到服务器返回的完整数据。
1.4、response.clone()
stream 对象只能读取一次,读取完就没了,这意味着,上边的五种读取方法,只能使用一个,否则会报错。
因此 response 对象提供了 clone() 方法,创建 respons 对象副本,实现多次读取。如下:将一张图片,读取两次:
const response1 = await fetch('flowers.jpg');
const response2 = response1.clone();
const myBlob1 = await response1.blob();
const myBlob2 = await response2.blob();
image1.src = URL.createObjectURL(myBlob1);
image2.src = URL.createObjectURL(myBlob2);
1.4、response.body()
body 属性返回一个 ReadableStream 对象,供用户操作,可以用来分块读取内容,显示下载的进度就是其中一种应用。
const response = await fetch('flower.jpg');
const reader = response.body.getReader();
while(true) {
const {done, value} = await reader.read();
if (done) {
break;
}
console.log(`Received ${value.length} bytes`)
}
response.body.getReader() 返回一个遍历器,这个遍历器 read() 方法每次都会返回一个对象,表示本次读取的内容块。
请求方式不同,传值方式也不同。xhr 会分别处理 get 和 post 数据传输,还有请求头设置,同样 fetch 也需要分别处理。
2.1、get 方式
只需要在url中加入传输数据,options中加入请求方式。如下面代码所示:
<input type="text" id="user"><br>
<input type="password" id="pas"><br>
<button onclick="login()">提交</button>
<script>
function login(){
fetch(`http://localhost:80/fetch.html?user=${user.value}&pas=${pas.value}`,{
method:'GET'
}).then(response=>{
console.log('响应',response)
})
}
</script>
2.2、post 方式
使用 post 发送请求时,需要设置请求头、请求数据等。
将上个实例,改写成 post 方式提交数据,代码如下:
fetch(`http://localhost:80/ES6练习题/53fetch.html`,{
method:'POST',
headers:{
'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8'
},
body:`user=${user.value}&pas=${pas.value}`
}).then(response=>{
console.log('响应',response)
})
如果是提交json数据时,需要把json转换成字符串。如
body:JSON.stringify(json)
如果提交的是表单数据,使用 formData转化下,如:
body:new FormData(form)
上传文件,可以包含在整个表单里一起提交,如:
const input = document.querySelector('input[type="file"]');
const data = new FormData();
data.append('file', input.files[0]);
data.append('user', 'foo');
fetch('/avatars', {
method: 'POST',
body: data
});
上传二进制数据,将 bolb 或 arrayBuffer 数据放到body属性里,如:
let blob = await new Promise(resolve =>
canvasElem.toBlob(resolve, 'image/png')
);
let response = await fetch('/article/fetch/post/image', {
method: 'POST',
body: blob
});
3.1、fetch兼容性
fetch原生支持率如图:
fetch 是相对较新的技术,IE浏览器不支持,还有其他低版本浏览器也不支持,因此如果使用fetch时,需要考虑浏览器兼容问题。
解决办法:引入 polyfill 完美支持 IE8 以上。
polyfill 的原理就是探测fetch是否支持,如果不支持则用 xhr 实现。支持 fetch 的浏览器,响应中文会乱码,所以使用 fetch-detector 和 fetch-ie8 解决乱码问题。
3.2、fetch默认不带cookie
传递cookie时,必须在header参数内加上 credentials:'include',才会像 xhr 将当前cookie 带有请求中。
3.3、异常处理
fetch 不同于 xhr ,xhr 自带取消、错误等方法,所以服务器返回 4xx 或 5xx 时,是不会抛出错误的,需要手动处理,通过 response 中的 status 字段来判断。
avaScript 初学者学完语法,进入实际的网页编程,一定有人告诉你,要掌握一个叫做 XMLHttpRequest 的东西。
脚本都靠它发出 HTTP 请求,跟服务器通信。所谓的 AJAX 操作就是基于它实现的。要是没有它,就不会有单页应用。
这个 XMLHttpRequest 对象,不仅名字怪,用法也非常独特。
let xhr = new XMLHttpRequest;
xhr.open('GET', url)
xhr.onload = function {
if (this.status === 200) {
let data = JSON.parse(this.responseText);
console.log(data);
}
};
xhr.onerror = function (err) {
console.log('Error Occurred :', err);
}
xhr.send;
就是因为写起来麻烦,实际开发中,往往使用它的各种封装库,比如早期的 jQuery。
$.get("test.php", function (data) {
$("body")
.append("Name: " + data.name)
.append("Time: " + data.time);
}, "json");
早期的封装库都使用回调函数,很不直观。后来的封装库都改用 Promise 的写法,比如 axios。
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
这样用起来,确实方便了,但是需要加载外部库,而它又是一个天天用到的基本需求。所以,浏览器厂商最终决定,抛弃 XMLHttpRequest,另起炉灶,重写一套全新的 API,内置在浏览器里面。
这就是 Fetch API,它使用fetch
发出请求,返回的是 Promise 对象。
fetch('https://api.github.com/users/ruanyf')
.then(response => response.json)
.then(json => console.log(json))
.catch(err => console.log('Request Failed', err));
Promise 可以使用 await 语法,表达起来跟同步操作一模一样。
async function getJSON {
let url = 'https://api.github.com/users/ruanyf';
try {
let response = await fetch(url);
return await response.json;
} catch (error) {
console.log('Request Failed', error);
}
}
Fetch API 的优点是写法简单,缺点是这个 API 包含的内容非常多,想要完全掌握并不容易。
我一直想写一篇教程,完整介绍它的各种用法,便于自己使用的时候查找。现在终于写出来,以后可以告别 XMLHttpRequest 了。
http://www.ruanyifeng.com/blog/2020/12/fetch-tutorial.html
(完)
etch()方法
ECMAscript官方继续推出了一个基于Promise的请求方法, 更简单, 更便捷。
就是fetch()方法,这个方法可以替代传统Ajax请求, 返回Promise的实例。
fetch()函数是ECMAscript提出的新的特性, 它:
能够发出Ajax请求, 并且请求原理不是xhr, 而是新的原理;
天生可以跨域, 但是需要设置头, 服务器可以写脚本识别这个头部判断是否应该拒绝它;
fetch()返回Promise对象, 所以可以用then和catch方法, then方法的第一个函数是resolve, 这个resolve的返回值将自动被await等号左边的变量的接收。
必须写async和await。
fetch的语法
fetch(url)
.then(res => console.log(res))
.then(res => console.log(res))
.catch(err => console.log(err));
使用场景1
async function main(){
//请求回来的数据都存在body中,作为一种可读的流返回,要使用json方法转换
var data1 = await fetch("data/1.json").then(data=>data.json());
var data2 = await fetch("data/2.json").then(data=>data.json());
var data3 = await fetch("data/3.json").then(data=>data.json());
console.log(data1)
console.log(data2)
console.log(data3)
};
main();
使用场景2
// 请求JSON数据
async function getUsers(){
const res = await fetch("https://www.xxx.com");
const data = await res.json();
return data;
}
getUsers()
.then((users) => console.log(users))
.catch((err) => console.log(err))
可以不用jQuery了!传统Ajax已死,fetch永生。
兼容性注意:
babel没有任何插件可以翻译fetch为传统Ajax。
fetch只能用在浏览器环境, 不能用在Nodejs环境。
fetch请求的两种数据格式
html结构:
<div>
<button id="btn1">请求本地文本数据</button>
<button id="btn2">请求网络json数据</button>
<div id="output"></div>
</div>
JavaScript逻辑:
<script>
document.getElementById('btn1').addEventListener('click', getText);
document.getElementById('btn2').addEventListener('click', getJson);
// 获取本地纯文本数据
function getText(){
fetch("test.txt").then(res => res.text())
.then(data => {console.log(data)})
.catch(err => console.log(err));
}
// 获取json数据
function getJson(){
fetch("posts.json")
.then(res => res.json())
.then(data => { console.log(data)})
.catch(err => console.log(err));
}
</script>
fetch方法的配置
fetch()方法的第二个参数是配置参数, 可以配置发送POST/PUT/DELETE等请求类型。
fetch('url', [options]);
实践中, post请求会像下面这样:
fetch('url', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({name:'小明'})
})
.then(data=>{console.log(data)})
.catch(err=>{console.log(err)})
method请求类型参数:
设置请求方式(如POST | PUT | DELETE), fetch默认请求方式为GET。
headers请求头参数
向服务器说明希望的到什么待遇, 这里主要用到Content-Type
因为一般使用JSON数据格式, 所以设置Content-Type为application/json
body参数设置报文体
要发送给后端的内容, 如果发送JSON数据, 必须调用JSON.stringify()方法。
credentials证书参数
因为默认情况下, fetch不会从服务端发送或接收任何cookies, 所以可以配置以下参数指定发送cookies的情况, 默认值:same-origin, 以下为可选值:
omit: 不发送cookies
same-origin: 在同域情况下发送cookies
include: 同域或跨域都发送cookies
可以理解为:
跨域请求是否提供凭据信息(cookie、HTTP认证及客户端SSL证明等)
也可以简单的理解为, 当前请求为跨域类型时是否在请求中协带cookie。
基于Promise封装fetch请求库
用ES6的class类封装一个函数
app.js
/*
* 基于Promise封装fetch请求库
*/
class FetchHttp{
// 封装get请求
get(url){
return new Promise((resolve,reject)=>{
fetch(url)
.then(data => resolve(data.json()))
.catch(err => reject(err))
})
}
// 封装post请求
post(url, data){
return new Promise((resolve, reject)=>{
fetch(url, {
method:"POST",
headers:{'Content-type':'application/json'},
body:JSON.stringify(data)
})
.then(data => resolve(data.json()))
.catch(err => reject(err))
})
}
// 封装put请求
put(url, data){
return new Promise((resolve, reject)=>{
fetch(url, {
method:"PUT",
headers:{'Content-type':'application/json'},
body:JSON.stringify(data)
})
.then(data => resolve(data.json()))
.catch(err => reject(err))
})
}
// 封装delete请求
delete(url){
return new Promise((resolve, reject)=>{
fetch(url, {
method:"DELETE",
headers:{'Content-type':'application/json'},
})
.then(data => resolve('数据删除成功!'))
.catch(err => reject(err))
})
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
</body>
<script src="js/app.js"></script>
<script>
const http = new FetchHttp; //引入函数
const data = {
name:"小明",
sex:"男",
age:29
}
http.delete("https://www.xxx.com")
.then(data=>{console.log(data)})
.catch(err=>{console.log(err)})
</script>
</html>
基于async封装fetch请求库app.js
*请认真填写需求信息,我们会在24小时内与您取得联系。