dwqs / dwqs/blog

Fetch围观指南

Open
#7 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
3.8k
Forks
443
PR merge metrics
No merged PRs in 30d

Description

在 Web 应用中,JavaScript 通过 [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) (XHR)来执行异步请求,这是一种有效改进页面通信的技术,当我们谈及Ajax技术的时候,通常意思就是基于 `XMLHttpRequest` 的 Ajax。虽说 Ajax 很有用,但它不是最佳 API,它在设计上不符合职责分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里。而且,基于事件的模型与现在 JavaScript 流行的 Promise 以及基于生成器的异步编程模型相背驰。本文将要介绍的内容则是XMLHttpRequest 的最新替代技术—— [Fetch API](https://fetch.spec.whatwg.org/), 它是 W3C 的正式标准。
## 兼容性

在介绍之前,先看看目前主流浏览器对 Fetch API 的支持情况:

![image](https://cloud.githubusercontent.com/assets/7871813/17837610/135768e8-67ea-11e6-93ac-abc4b89c05cd.png)

Fetch 的支持目前还处于早期的阶段,在 Firefox 39 以上,和 Chrome 42 以上都被支持了。

如果你现在就想使用它,还可以用 [Fetch Polyfil](https://github.com/github/fetch),用于支持那些还未支持 Fetch 的浏览器。

在使用 Fetch 之前,也可以对其进行功能性检测:

```
if(self.fetch) {
// run my fetch request here
} else {
// do something with XMLHttpRequest?
}
```
## 简单的fetching示例

在 [Fetch API](https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch) 中,最常用的就是 fetch() 函数。它接收一个URL参数,返回一个 promise 来处理 response。response 是一个 Response 对象:

```
fetch("/data.json").then(function(res) {
// res instanceof Response == true.
if (res.ok) {
res.json().then(function(data) {
console.log(data.entries);
});
} else {
console.log("Looks like the response wasn't perfect, got status", res.status);
}
}, function(e) {
console.log("Fetch failed!", e);
});
```

fetch() 接受第二个可选参数,一个可以控制不同配置的 init 对象。如果是提交一个 POST 请求,代码如下:

```
fetch("http://www.example.org/submit.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "firstName=Nikhil&favColor=blue&password=easytoguess"
}).then(function(res) {
if (res.ok) {
//res.ok用于检测请求是否成功
console.log("Perfect! Your settings are saved.");
} else if (res.status == 401) {
console.log("Oops! You are not authorized.");
}
}, function(e) {
console.log("Error submitting form!");
});
```

如果遇到网络故障,fetch() promise 将会 reject,带上一个 TypeError 对象。想要精确的判断 fetch() 是否成功,需要包含 promise resolved 的情况,此时再判断 [Response.ok](https://developer.mozilla.org/zh-CN/docs/Web/API/Response/ok) 是不是为 `true`。

Fetch 实现了四个接口:GlobalFetch、Headers、Request 和 Response。GloabaFetch 就只包含了一个 `fetch` 方法用于获取网络资源,其它三个直接对应了相应的 HTTP 概念。此外,在 request/reponse 中,还混淆了 Body。
## Headers

[Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) 接口允许定义 HTTP 的请求头([Request.headers](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers))和响应头([Response.headers](https://developer.mozilla.org/en-US/docs/Web/API/Response/headers))。一个 Headers 对象是一个简单的多名值对:

```
var content = "Hello World";
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Content-Length", content.length.toString());
myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
```

也可以传一个多维数组或者对象字面量:

```
myHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "ProcessThisImmediately",
});
```

此外,Headers 接口提供了 `set` ,`delete` 等 [API](https://developer.mozilla.org/en-US/docs/Web/API/Headers) 用于检索其内容:

```
console.log(reqHeaders.has("Content-Type")); // true
console.log(reqHeaders.has("Set-Cookie")); // false
reqHeaders.set("Content-Type", "text/html");
reqHeaders.append("X-Custom-Header", "AnotherValue");

console.log(reqHeaders.get("Content-Length")); // 11
console.log(reqHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]

reqHeaders.delete("X-Custom-Header");
console.log(reqHeaders.getAll("X-Custom-Header")); // []
```

虽然有些操作仅在 ServiceWorkers 中使用,但相对于 XHR,其本身提供了非常方便的操作 Headers 的 API。

出于安全原因,有些 header 字段的设置仅能通过 User Agent 实现,不能通过编程设置:[请求头禁置字段](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name) 和 [响应头禁置字段](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_response_header_name)。

如果使用了一个不合法的 HTTP Header 属性名或者写入一个不可写的属性,Headers 的方法通常都抛出 TypeError 异常:

```
var myResponse = Response.error();
try {
myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
console.log("Cannot pretend to be a bank!");
}
```

最佳实践是在使用之前检查 content type 是否正确,比如:

```
fetch(myRequest).then(function(response) {
if(response.headers.get("content-type") === "application/json") {
return response.json().then(function(json) {
// process your JSON further
});
} else {
console.log("Oops, we haven't got JSON!");
}
});
```

由于 Headers 可以在 request 请求中被发送或者在 response 请求中被接收,并且规定了哪些参数是可写的,Headers 对象有一个特殊的 **guard** 属性。这个属性没有暴露给 Web,但是它影响到哪些内容可以在 Headers 对象中被改变。

可能的值如下:
- none:默认的
- request:从 request 中获得的 headers(Request.headers)只读
- request-no-cors:从不同域(Request.mode no-cors)的 request 中获得的 headers 只读
- response:从 response 中获得的 headers(Response.headers)只读
- immutable:在 ServiceWorkers 中最常用的,所有的 headers 都只读
## Request

[Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) 接口定义了通过HTTP请求资源的request格式,一个简单请求构造如下:

```
var req = new Request("/index.html");
console.log(req.method); // "GET"
console.log(req.url); // "http://example.com/index.html"
console.log(req.headers); //请求头
```

和 fetch() 一样,Request 接受第二个可选参数,一个可以控制不同配置的 init 对象:

```
var myHeaders = new Headers();

var myInit = { method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default' ,
credentials: true,
body: "image data"};

var myRequest = new Request('flowers.jpg',myInit);

fetch(myRequest,myInit)
.then(function(response) {
return response.blob();
})
.then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
```

**mode** 属性用来决定是否允许跨域请求,以及哪些response 属性可读。mode 可选的属性值:
- same-origin:请求遵循[同源策略](http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html)
- no-cors: 默认值,允许来自CDN的脚本、其他域的图片和其他一些跨域资源(前提条件是 method 只能是"HEAD","GET"或者"POST")
- cors :允许跨域,请求遵循 [CROS协议](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)

**credentials** 枚举属性决定了cookies 是否能跨域得到,这与 XHR 的 `withCredentials` 标志相同,但是只有三个值,分别是"omit"(默认),"same-origin"以及"include"。
## Response

[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) 实例是在 fentch() 处理完 promises 之后返回的,它的实例也可用通过 JavaScript 来创建, 但只有在 ServiceWorkers 中才真正有用。当使用 [respondWith()](https://developer.mozilla.org/zh-CN/docs/Web/API/FetchEvent/respondWith) 方法并提供了一个自定义的response来接受request时:

```
var myBody = new Blob();

addEventListener('fetch', function(event) {
event.respondWith(new Response(myBody, {
headers: { "Content-Type" : "text/plain" }
});
});
```

Response() 构造方法接受两个可选参数—response的数据体和一个初始化对象(与 Request() 所接受的init参数类似.)

最常见的response属性有:
- Response.status — 整数(默认值为200) 为response的状态码.
- Response.statusText — 字符串(默认值为"OK"),该值与HTTP状态码消息对应.
- Response.ok — 如上所示, 该属性是来检查response的状态是否在200-299(包括200,299)这个范围内.该属性返回一个Boolean值.
- Response.headers — 响应头
- Response.type — 响应[类型](https://developer.mozilla.org/en-US/docs/Web/API/Response/type),如:basic/ cors /error
## Body

Request 和 Response 都实现了 [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body) 接口,在请求过程中,二者都会携带 Body,其可以是以下任何一种类型的实例:
- [ArrayBuffer](https://developer.mozilla.org/zh-CN/docs/Web/API/ArrayBuffer)
- [ArrayBufferView](https://developer.mozilla.org/zh-CN/docs/Web/API/ArrayBufferView)
- [Blob](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)/file
- [URLSearchParams](https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams)
- [FormData](https://developer.mozilla.org/zh-CN/docs/Web/API/FormData)

此外,Request 和 Response 都为他们的body提供了以下方法,这些方法都返回一个Promise对象:
- arrayBuffer()
- blob()
- json()
- text()
- formData()
## 相关阅读

[Using Fetch](https://developer.mozilla.org/en-us/docs/Web/API/Fetch_API/Using_Fetch)
[Asynchronous APIs Using the Fetch API and ES6 Generators](http://www.sitepoint.com/asynchronous-apis-using-fetch-api-es6-generators/)
[This API is so fetching](https://hacks.mozilla.org/2015/03/this-api-is-so-fetching)

* [chrome.notifications - Google Chrome 应用开发文档(非官方中文版)](https://crxdoc-zh.appspot.com/apps/notifications#type-TemplateType)

Contributor guide

No contributing guide indexed for this repository

Research direction

The issue contains a Chinese Fetch API article but names no target file, test, or entry point. First identify where blog articles are stored and confirm the intended scope with maintainers; the issue does not define what done looks like.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
documentation
Issue type
Documentation
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.