看着2019年过半,立的flag还剩好多,所以尽量之后能2周一篇的速度来敢一下进度,完成今年的计划。
一、Fetch API
Fetch API
目前各浏览器的支持情况较好,和XHR
实现了相同的数据请求,在返回结果处理上,使用了Promise
,使其结果可以使用Promise
的链式写法
1. Fetch
相比于XHR
的创建过程,
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onreadystatechange = function(e) {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send(data);
Fetch
的使用步骤就简单很多:
fetch(url[,init]).then(res => {
return res.json();
}).then(data => {
console.log(data);
});
Fetch
对象接受两个参数,第一个参数可以是一个url
字符串,也可以是一个Request
对象;第二个参数接受一些初始化的options
(在Request
中详细说明)
fetch(url[,init])
fetch(new Request()[,init])
Fetch返回的结果为一个Promise
,resolve
值是一个Response
对象,有两个注意的地方:
Fetch请求结果即使http请求响应码为4xx和5xx的请求,也不会进行reject,除非网络请求失败或者请求受到阻碍
// node.js代码
http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.writeHead(404)
res.end('fail')
}).listen(9000)
// fetch
fetch('http://localhost:9000').then(res => {
console.log(res.text()) // 输出fail
}, err => {
console.log(err)
})
默认Fetch在跨域的时候不会携带cookie进行请求,如果需要携带cookie,需要设置
credentials
为include
(XHR
是设置withCredentials=true
),同时服务器需要配置Access-Control-Allow-Credentials: true
// node跨域配置
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080')
res.setHeader('Access-Control-Allow-Credentials', 'true')
// fetch配置
fetch('http://localhost:9000', {
credentials: 'include'
})
2. Request
Request
可以作为Fetch
的参数,通过new Request(input[, init])
构建一个完整的请求对象,input
可以是一个url
,也可以是一个复制的Request
对象
let request = new Request('http://localhost:9000');
fetch(request);
// 接受一个request
let request2 = new Request(request.clone())
fetch(request2);
Request
可以设置多个初始化参数:
2.1 method
请求的请求方式:get
,post
,delete
等
2.2 headers
请求头信息,可以是普通的对象字面量,也可以Headers
对象
2.3 body
请求携带的数据对象,如果method
是get
和head
会忽略掉该设置
2.4 mode
用于决定跨域请求的响应信息是否有效,值包括:
'same-origin':只允许同源请求,否则会提示错误。
'no-cors':只能使用
HEAD
,GET
,POST
请求,同时Headers
必须是simple headers的请求
'cors':允许跨域
2.5 credentials
用于决定cookie
的传递方式,默认跨域情况下是请求是不会传递cookie
的,可以设置的值包括:omit
(不携带cookie
),same-origin
(默认值,同源携带cookie
),include
(携带cookie
)
2.6 cache
用于设置浏览器对于请求结果的缓存机制,值包括:
default: 如果匹配或者fresh则从缓存获取,否则从远端获取
no-store: 不会检查cache,优先请求服务器,且本地不会更新cache
reload: 不会检查cache,优先请求服务器,但结果会更新cache
no-cache: 浏览器会发送conditional request请求服务器,判断cache是否更新,如果未更新,使用本地cache,否则从服务器重新获取数据,并更新本地cache
force-cache: 优先从cache获取,如果未找到匹配,浏览器发送普通请求,获取数据并进行cache
only-if-cached: 只从cache获取,如果未找到匹配,则返回
504 Gateway timeout
,只适用于mode设置为same-origin的情况
3. Headers
Headers对象用于替代字面量对象的方式,給Request
和Response
设置请求/响应的头信息,相比于字面量对象的方式,提供了较多的方法来处理headers
3.1 headers.set()
设置头信息中属性,如果已经存在该头信息,则会对其进行覆盖
let headers = new Headers();
headers.set('Content-Type', 'application/json');
console.log(headers.get('Content-Type')); // application/json
headers.set('Content-Type', 'plain/text');
console.log(headers.get('Content-Type')); // plain/text
3.2 headers.append()
用于追加头信息中属性,如果已经存在该信息,则会对其进行追加
let headers = new Headers();
headers.append('Content-Type', 'application/json');
console.log(headers.get('Content-Type')); // application/json
headers.append('Content-Type', 'plain/text');
console.log(headers.get('Content-Type')); // application/json, plain/text
3.3 headers.delete()
用于删除头信息中属性
let headers = new Headers();
headers.append('Content-Type', 'application/json');
console.log(headers.get('Content-Type')); // application/json
headers.delete('Content-Type');
console.log(headers.get('Content-Type')); // null
3.4 headers.get()
用于获取设置的头信息中属性的值
3.5 headers.has()
用于判断头信息中是否已经设置属性
let headers = new Headers();
console.log(headers.has('Content-Type')); // false
headers.append('Content-Type', 'application/json');
console.log(headers.has('Content-Type')); // true
3.6 values, keys, entries
用于遍历所有的headers的属性,都会返回一个iterator对象
let headers = new Headers();
headers.append('Content-Type', 'application/json');
for(let key of headers.keys()) {
console.log(key); // content-type
}
for(let val of headers.values()) {
console.log(val); // application/json
}
for(let entry of headers.entries()) {
console.log(entry); // ['content-type', 'application/json'}]
}
PS:headers值的有效性受到Request的mode值的影响。
4. Response
响应体对象,Fetch
返回Promise
的resolve
的值,也可以自行构建Response
对象(暂时没发现直接构建使用的用途),可以通过
new Response(body[,init])
的方式创建
body
是传递的参数,可以是各种数据类型: BufferSource
, Blob
, FormData
,
ReadableStream
,URLSearchParams
init
主要包括status
, statusText
, headers
等,其他几个常用
Response.ok
: 如果响应码在(200-299)返回true
Response.status
: 返回请求响应码,例如:200
Response.statusText
: 返回请求响应码文本,例如:200的响应码文本默认是'OK'
5. Body
Body
主要是Response
请求的结果,默认是ReadableStream
类型,Response
实现了json()
, text()
, arrayBuffer()
, blob()
, formData()
方法,可以直接将body
转化为对应数据类型
fetch('http://localhost:9000').then(res => res.json()) // 返回一个json类型数据
6. Abort
如何取消一个Fetch
的请求呢?现代浏览器提供了AbortControl
的类来处理,在Fetch
的时候,初始化参数可以接收一个signal
的参数,从而在AbortControl
出发abort
的时候,抛出异常终止Fetch
,详细可以参见参考中MDN上的相关说明
let controller = new AbortController();
let signal = controller.signal;
fetch(url, {signal}).then(res => {
}).catch(e => {
console.log(e);
})
controller.abort();
二、示例
一个简单的的关于Fetch API
使用的例子
// index.js - node
let http = require('http');
let fs = require('fs')
let server = http.createServer((req, res) => {
// 设置跨域
// res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080')
// 设置是否跨域cookie
// res.setHeader('Access-Control-Allow-Credentials', 'true')
// 返回文本类型响应
if (req.url.indexOf('/text') > -1) {
res.setHeader('Content-Type', 'plain/text')
res.end('test')
}
// 返回json类型响应
if (req.url.indexOf('/json') > -1) {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({text: 'test'}))
}
// 返回文件数据流
if (req.url.indexOf('/file/') > -1) {
// 通过截取后缀判断文件并返回文件内容作为请求输出
let fileName = req.url.substr(req.url.indexOf('/file/') + 6, req.url.length);
fs.stat(fileName, function (err, stats) {
if (!err && stats.isFile()) {
// 没有出错并且文件存在:
console.log('200 ' + req.url);
// 发送200响应:
res.writeHead(200);
// 将文件流导向response:
fs.createReadStream(fileName).pipe(res);
} else {
// 出错了或者文件不存在:
console.log('404 ' + req.url);
// 发送404响应:
res.writeHead(404);
res.end('404 Not Found');
}
});
}
});
server.listen(9000)
// test.js - client
// 使用Request创建一个请求
let req = new Request('http://localhost:9000/text')
fetch(req, {
credentials: 'omit', // 设置不传递cookie
mode: 'no-cors', // 设置请求不允许跨域
}).then(res => {
return res.text();
}).then(data => {
console.log(data)
})
三、总结
简单学习了Fetch API相关的内容,如果习惯使用Promise
带来的链式编程或者习惯使用async/await
,Fetch API
相比于使用XHR
有其特有优势,不过现在大家都习惯于直接使用第三方库(例如:axios)做数据请求,所以目前直接应用场景似乎不多
但是Fetch
也有自身局限性,比如:XHR
可以监听文件上传进度,但是目前Fetch API
并没有提供相关功能。