美文网首页饥人谷技术博客
Fetch:ajax替代品(译文)

Fetch:ajax替代品(译文)

作者: 种谔 | 来源:发表于2016-09-06 14:23 被阅读0次

    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

    相关文章

      网友评论

        本文标题:Fetch:ajax替代品(译文)

        本文链接:https://www.haomeiwen.com/subject/gikdettx.html