Skip to content

Commit 1d5495d

Browse files
committed
修改bom/ajax/fetch
1 parent bef9eef commit 1d5495d

File tree

3 files changed

+312
-59
lines changed

3 files changed

+312
-59
lines changed

bom/ajax.md

Lines changed: 263 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -615,67 +615,99 @@ CORS机制与JSONP模式的使用目的相同,而且更强大。JSONP只支持
615615

616616
## Fetch API
617617

618-
Fetch API是一种新兴的规范,用来操作浏览器发出的网络请求,目的是在将来取代XMLHttpRequest。它的作用与XMLHttpRequest类似,但是返回的是Promise对象,因此API大大简化,并且避免了嵌套的回调函数。
618+
### 基本用法
619+
620+
Ajax操作所用的XMLHttpRequest对象,已经有十多年的历史,它的API设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。Fetch API是一种新规范,用来取代XMLHttpRequest对象。它主要有两个特点,一是简化接口,将API分散在几个不同的对象上,二是返回Promise对象,避免了嵌套的回调函数。
621+
622+
检查浏览器是否部署了这个API的代码如下。
623+
624+
```javascript
625+
626+
if (fetch in window){
627+
// 支持
628+
} else {
629+
// 不支持
630+
}
631+
632+
```
619633

620634
下面是一个XMLHttpRequest对象发出Ajax请求的常规例子。
621635

622636
```javascript
623637

624-
function reqListener() {
625-
var data = JSON.parse(this.responseText);
626-
console.log(data);
638+
function reqListener() {
639+
var data = JSON.parse(this.responseText);
640+
console.log(data);
627641
}
628642

629-
function reqError(err) {
630-
console.log('Fetch Error :-S', err);
643+
function reqError(err) {
644+
console.log('Fetch Error :-S', err);
631645
}
632646

633-
var oReq = new XMLHttpRequest();
634-
oReq.onload = reqListener;
635-
oReq.onerror = reqError;
636-
oReq.open('get', './api/some.json', true);
647+
var oReq = new XMLHttpRequest();
648+
oReq.onload = reqListener;
649+
oReq.onerror = reqError;
650+
oReq.open('get', './api/some.json', true);
637651
oReq.send();
638652

639653
```
640654

641-
上面的例子用Fetch实现如下
655+
同样的操作用Fetch实现如下
642656

643657
```javascript
644658

645-
fetch('./api/some.json')
646-
.then(function(response) {
647-
if (response.status !== 200) {
648-
console.log('请求失败,状态码:' + response.status);
649-
return;
650-
}
651-
response.json().then(function(data) {
652-
console.log(data);
653-
});
654-
}).catch(function(err) {
655-
console.log('出错:', err);
659+
fetch('./api/some.json')
660+
.then(function(response) {
661+
if (response.status !== 200) {
662+
console.log('请求失败,状态码:' + response.status);
663+
return;
664+
}
665+
response.json().then(function(data) {
666+
console.log(data);
667+
});
668+
}).catch(function(err) {
669+
console.log('出错:', err);
656670
});
657671

658-
```
672+
```
659673

660674
上面代码中,因为HTTP请求返回的response对象是一个Stream对象,使用json方法转为JSON格式,但是json方法返回的是一个Promise对象。
661675

676+
fetch函数的第一个参数可以是URL字符串,也可以是后文要讲到的Request对象实例。
677+
678+
response对象还有一个ok属性,如果返回的状态码在200到299之间(即请求成功),这个属性为true,否则为false。因此,上面的代码可以写成下面这样。
679+
680+
```javascript
681+
fetch("./api/some.json").then(function(response) {
682+
if (response.ok) {
683+
response.json().then(function(data) {
684+
console.log(data);
685+
});
686+
} else {
687+
console.log("请求失败,状态码为", response.status);
688+
}
689+
}, function(err) {
690+
console.log("出错:", err);
691+
});
692+
```
693+
662694
response对象除了json方法,还包含了HTTP回应的元数据。
663695

664696
```javascript
665697

666-
fetch('users.json').then(function(response) {
667-
console.log(response.headers.get('Content-Type'));
698+
fetch('users.json').then(function(response) {
699+
console.log(response.headers.get('Content-Type'));
668700
console.log(response.headers.get('Date'));
669701

670-
console.log(response.status);
671-
console.log(response.statusText);
672-
console.log(response.type);
673-
console.log(response.url);
702+
console.log(response.status);
703+
console.log(response.statusText);
704+
console.log(response.type);
705+
console.log(response.url);
674706
});
675707

676708
```
677709

678-
上面代码中,response.type表示HTTP回应的类型,它有以下三个值。
710+
上面代码中,response对象有很多属性,其中的type属性比较特别,表示HTTP回应的类型,它有以下三个值。
679711

680712
- basic:正常的同域请求
681713
- cors:CORS机制下的跨域请求
@@ -702,8 +734,8 @@ fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
702734

703735
```javascript
704736

705-
fetch(url, {
706-
credentials: 'include'
737+
fetch(url, {
738+
credentials: 'include'
707739
})
708740

709741
```
@@ -712,21 +744,206 @@ fetch(url, {
712744

713745
```javascript
714746

715-
fetch(url, {
716-
method: 'post',
717-
headers: {
718-
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
719-
},
720-
body: 'foo=bar&lorem=ipsum'
721-
})
722-
.then(json)
723-
.then(function (data) {
724-
console.log('Request succeeded with JSON response', data);
725-
})
726-
.catch(function (error) {
727-
console.log('Request failed', error);
747+
fetch("http://www.example.org/submit.php", {
748+
method: "POST",
749+
headers: {
750+
"Content-Type": "application/x-www-form-urlencoded"
751+
},
752+
body: "firstName=Nikhil&favColor=blue&password=easytoguess"
753+
}).then(function(res) {
754+
if (res.ok) {
755+
console.log("Perfect! Your settings are saved.");
756+
} else if (res.status == 401) {
757+
console.log("Oops! You are not authorized.");
758+
}
759+
}, function(e) {
760+
console.log("Error submitting form!");
761+
});
762+
763+
```
764+
765+
目前,还有一些XMLHttpRequest对象可以做到,但是Fetch API还没做到的地方,比如中途中断HTTP请求,以及获取HTTP请求的进度。这些不足与Fetch返回的是Promise对象有关。
766+
767+
### Headers
768+
769+
Fetch API引入三个新的对象(也是构造函数):Headers, Request 和 Response。其中,Headers对象用来构造/读取HTTP数据包的头信息。
770+
771+
```javascript
772+
var content = "Hello World";
773+
var reqHeaders = new Headers();
774+
reqHeaders.append("Content-Type", "text/plain");
775+
reqHeaders.append("Content-Length", content.length.toString());
776+
reqHeaders.append("X-Custom-Header", "ProcessThisImmediately");
777+
```
778+
779+
Headers对象的实例,除了使用append方法添加属性,也可以直接通过构造函数一次性生成。
780+
781+
```javascript
782+
reqHeaders = new Headers({
783+
"Content-Type": "text/plain",
784+
"Content-Length": content.length.toString(),
785+
"X-Custom-Header": "ProcessThisImmediately",
786+
});
787+
```
788+
789+
Headers对象实例还提供了一些工具方法。
790+
791+
```javascript
792+
reqHeaders.has("Content-Type") // true
793+
reqHeaders.has("Set-Cookie") // false
794+
reqHeaders.set("Content-Type", "text/html")
795+
reqHeaders.append("X-Custom-Header", "AnotherValue")
796+
797+
reqHeaders.get("Content-Length") // 11
798+
reqHeaders.getAll("X-Custom-Header") // ["ProcessThisImmediately", "AnotherValue"]
799+
800+
reqHeaders.delete("X-Custom-Header")
801+
reqHeaders.getAll("X-Custom-Header") // []
802+
```
803+
804+
### Request
805+
806+
Request对象用来构造HTTP请求。
807+
808+
```javascript
809+
var req = new Request("/index.html");
810+
req.method // "GET"
811+
req.url // "http://example.com/index.html"
812+
```
813+
814+
Request对象实例的非url属性,只能通过Request构造函数的第二个参数进行设置。
815+
816+
```javascript
817+
var uploadReq = new Request("/uploadImage", {
818+
method: "POST",
819+
headers: {
820+
"Content-Type": "image/png",
821+
},
822+
body: "image data"
823+
});
824+
```
825+
826+
Request对象实例有两个属性是只读的,不能手动设置。一个是referrer属性,表示请求的来源,由浏览器设置,有可能是空字符串。另一个是context属性,表示请求发出的上下文,如果是image,表示是从img标签发出,如果是worker,表示是从worker脚本发出,如果是fetch,表示是从fetch函数发出的。
827+
828+
Request对象实例的mode属性,用来设置是否跨域,合法的值有以下三种:same-origin、no-cors(默认值)、cors。当设置为same-origin时,只能向同域的URL发出请求,否则会报错。
829+
830+
```javascript
831+
var arbitraryUrl = document.getElementById("url-input").value;
832+
fetch(arbitraryUrl, { mode: "same-origin" }).then(function(res) {
833+
console.log("Response succeeded?", res.ok);
834+
}, function(e) {
835+
console.log("Please enter a same-origin URL!");
836+
});
837+
```
838+
839+
上面代码中,如果用户输入的URL不是同域的,将会报错,否则就会发出请求。
840+
841+
如果mode属性为no-cors,就与默认的浏览器行为没有不同,类似script标签加载外部脚本文件、img标签加载外部图片。如果mode属性为cors,就可以向部署了CORS机制的服务器,发出跨域请求。
842+
843+
```javascript
844+
var u = new URLSearchParams();
845+
u.append('method', 'flickr.interestingness.getList');
846+
u.append('api_key', '<insert api key here>');
847+
u.append('format', 'json');
848+
u.append('nojsoncallback', '1');
849+
850+
var apiCall = fetch('https://api.flickr.com/services/rest?' + u);
851+
852+
apiCall.then(function(response) {
853+
return response.json().then(function(json) {
854+
// photo is a list of photos.
855+
return json.photos.photo;
856+
});
857+
}).then(function(photos) {
858+
photos.forEach(function(photo) {
859+
console.log(photo.title);
728860
});
861+
});
862+
```
863+
864+
上面代码是向Flickr API发出图片请求的例子。
865+
866+
### Response
867+
868+
fetch方法返回Response对象实例,它有以下属性。
869+
870+
- status:整数值,表示状态码(比如200)
871+
- statusText:字符串,表示状态信息,默认是“OK”
872+
- ok:布尔值,表示状态码是否在200-299的范围内
873+
- headers:Headers对象,表示HTTP回应的头信息
874+
- url:字符串,表示HTTP请求的网址
875+
- type:字符串,合法的值有五个basic、cors、default、error、opaque。basic表示正常的同域请求;cors表示CORS机制的跨域请求;error表示网络出错,无法取得信息,status属性为0,headers属性为空,并且导致fetch函数返回Promise对象被拒绝;opaque表示非CORS机制的跨域请求,受到严格限制。
876+
877+
Response对象还有两个静态方法。
878+
879+
- Response.error() 返回一个type属性为error的Response对象实例
880+
- Response.redirect(url, status) 返回的Response对象实例会重定向到另一个URL
881+
882+
### body属性
729883

884+
Request对象和Response对象都有body属性,表示请求的内容。body属性可能是以下的数据类型。
885+
886+
- ArrayBuffer
887+
- ArrayBufferView (Uint8Array等)
888+
- Blob/File
889+
- string
890+
- URLSearchParams
891+
- FormData
892+
893+
```javascript
894+
var form = new FormData(document.getElementById('login-form'));
895+
fetch("/login", {
896+
method: "POST",
897+
body: form
898+
})
899+
```
900+
901+
上面代码中,Request对象的body属性为表单数据。
902+
903+
Request对象和Response对象都提供以下方法,用来读取body。
904+
905+
- arrayBuffer()
906+
- blob()
907+
- json()
908+
- text()
909+
- formData()
910+
911+
注意,上面这些方法都只能使用一次,第二次使用就会报错,也就是说,body属性只能读取一次。Request对象和Response对象都有bodyUsed属性,返回一个布尔值,表示body是否被读取过。
912+
913+
```javascript
914+
var res = new Response("one time use");
915+
console.log(res.bodyUsed); // false
916+
res.text().then(function(v) {
917+
console.log(res.bodyUsed); // true
918+
});
919+
console.log(res.bodyUsed); // true
920+
921+
res.text().catch(function(e) {
922+
console.log("Tried to read already consumed Response");
923+
});
924+
```
925+
926+
上面代码中,第二次通过text方法读取Response对象实例的body时,就会报错。
927+
928+
这是因为body属性是一个stream对象,数据只能单向传送一次。这样的设计是为了允许JavaScript处理视频、音频这样的大型文件。
929+
930+
如果希望多次使用body属性,可以使用Response对象和Request对象的clone方法。它必须在body还没有读取前调用,返回一个前的body,也就是说,需要使用几次body,就要调用几次clone方法。
931+
932+
```javascript
933+
addEventListener('fetch', function(evt) {
934+
var sheep = new Response("Dolly");
935+
console.log(sheep.bodyUsed); // false
936+
var clone = sheep.clone();
937+
console.log(clone.bodyUsed); // false
938+
939+
clone.text();
940+
console.log(sheep.bodyUsed); // false
941+
console.log(clone.bodyUsed); // true
942+
943+
evt.respondWith(cache.add(sheep.clone()).then(function(e) {
944+
return sheep;
945+
});
946+
});
730947
```
731948
732949
## 参考链接
@@ -738,3 +955,4 @@ fetch(url, {
738955
- Monsur Hossain, [Using CORS](http://www.html5rocks.com/en/tutorials/cors/)
739956
- MDN, [HTTP access control (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
740957
- Matt Gaunt, [Introduction to fetch()](http://updates.html5rocks.com/2015/03/introduction-to-fetch)
958+
- Nikhil Marathe, [This API is so Fetching!](https://hacks.mozilla.org/2015/03/this-api-is-so-fetching/)

0 commit comments

Comments
 (0)