Fetch:ajax替代品(译文)

Fetch概念

fetch身为H5中的一个新对象,他的诞生,是为了取代ajax的存在而出现,主要目的仅仅只是为了结合ServiceWorkers,来达到以下优化:

  1. 优化离线体验
  2. 保持可扩展性

当然如果ServiceWorkers和浏览器端的数据库IndexedDB配合,那么恭喜你,每一个浏览器都可以成为一个代理服务器一样的存在。(然而我并不认为这样是好事,这样会使得前端越来越重,走以前c/s架构的老路)

能力检测

如果你想知道自己的浏览器是否支持Fetch,只需要简单new Request或new Response或 new Headers试试就知道了。对于这三个对象是不是非常眼熟,对没错,这就是http三剑客,请求,响应和头对象。

当然那些检测还是很麻烦,最简单实用的就是这样做

        if (window.fetch) {
            //用fetch做一些事情
         }else{
           //用ajax做一些事情
         }

注意:文章通篇在对比的是fetch和原生ajax(既XMLHttpRequest),而不是jq中被包装过的ajax。

简单的fetch请求

现在我们来设置一个非常简单且基本的fetch请求,如下代码:

var img =document.querySelector("img");

fetch('flower.jpg').then(function(response){
        return response.blob();
}).then(function(myblob){
        var objectURL = URL.createObjectURL(myBlob);
        myImage.src =objectURL;
});

当然你要是看不懂这个写法,也没有关系,可以先去学习下ES6中promise的用法,或者promise/A+规范;

在这边我们请求了一个flower.jpg的图片,请求完成之后用URL.createObjectURL的方式转化为一个url,最后把它赋予img节点的src属性。

当然这边的response对象的blob方法返回的是一个promise对象,所以可以这样链式调用。

注意:如果不做特别设置,默认情况下是get方法,如果想要使用别的方法,或者需要设置特别的http头什么的,则需要用到headers和request对象,或者fetch的额外选项(感觉只是对headers和request的包装)。

设置request的fetch请求

你可以通过request的构造函数新建一个request对象,并把它做为参数传入fetch中,类似下面这样:

    var myHeaders = new Headers();
    var myInit = { 
                      method: 'GET', 
                      headers: myHeaders, 
                      mode: 'cors', 
                      cache: 'default' 
                   };
    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;
            });

当然我们也可以通过把一个旧的request对象传入一个request的构造函数,这样的我们就可以获得一个拷贝之后的request对象。

var anotherRequest = new Request(myRequest,myInit);

Headers对象

Headers对象允许你通过Header构造函数来创建,一个Headers对象只是一个简单的键值对集合的map,当然,里面的键值对必须符合http协议。(由此可见《http权威指南》是一本好书)。

  • 设置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");
  • 设置Headers对象内容的方式二:
myHeaders = new Headers({
         "Content-Type": "text/plain",
         "Content-Length": content.length.toString(), 
        "X-Custom-Header": "ProcessThisImmediately"
        });

当然Headers里面的内容是可以查询和设置:

console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue"); 
console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"] 
myHeaders.delete("X-Custom-Header");
console.log(myHeaders.getAll("X-Custom-Header")); // [ ]

但是这里面的有些操作仅仅在 ServiceWorkers
中有用。

如果你对header的key设置了一个不符合http协议规范中的key,(比如你对request的header设置了response特有的header),那么js在严格守护的模式下会报TypeError的错误。例如:

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

一个比较好的用例,就是在处理数据之前,先检查下response中的content-type值。例如:

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

Guard(守护模式)

每一个Headers对象都有一个Guard的属性,该属性控制着该头节点是否可以设置别的key-value值(键值对)。目前在web中Guard的属性并没有被暴露出来,因此你在浏览器中是无法获得该属性的。

Guard拥有以下几个属性

none: 默认的.
request: 对request对象守护 (Request.headers).
request-no-cors: 设置Request.mode 的值为no-cors,request中的headers守护模式值。
response:Response.headers的守护级别。
immutable: 经常用于ServiceWorkers; headers 变为只读。

** 注意:你也许无法在requeset中设置"Content-Length",类似的,在response中无法设置"Set-Cookie",因为在ServiceWorkers中是不被允许设置cookie的。

Response

正如你在上面看到的,response只有在fetch对象的状态处于resolved的情况下才会在返回。(其实是只有当fetch这个异步操作成功之后,response对象会作为一个参数,传入到回掉函数中。)

当然我们也可以通过Response的构造函数来实现,但是这个对象只有在ServiceWorkers中是有用的,当然你也可以在window中监听fetch事件,然后调用event的responseWith方法中使用,如下:

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

以下是我们经常用的到的response属性:

** 注意:Response的静态方法中 error()redirect()返回的是也都是Response对象,但是只有在Service Workers中有用。

Body

这边的body指的是请求和响应的体,支持一下几种数据类型:

当然body都有相对的扩展方法去获取这些类型的数据,这些方法返回的值是一个promise对象,基本上都是基于stream(流)的思想。

于是我们就可以用以下的方式来使用fetch:

var form = new FormData(document.getElementById('login-form'));
fetch("/login", { 
    method: "POST", 
    body: form
    })

不要担心在传这些数据的时候,Content-type的值,浏览器会智能的帮我们设置这些,不管在request中还是response中,当然你要是手工指定也是可以的。

总结

fetch相比较原生ajax更为灵活,提供的数据类型更多,更接近底层。但是目前看来,想要取代ajax,还需要一段时间,因为ajax二代+类jQuery工具库的帮助,fetch在常规应用方面还是相形见绌的。

参考资料:
Using Fetch
[译] JavaScript Fetch API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容