浏览器-Fetch

1. Fetch

在开发过程中,我们向服务端发送请求,一般会使用三种方式, XMLHttpRequest(XHR),Fetch ,jQuery实现的AJAX。
其中, XMLHttpRequest(XHR)和Fetch是浏览器的原生API,jquery的ajax其实是封装了XHR。
让我们首先来比较一下这三者的使用示例。

XMLHttpRequest
var xhr;
if (window.XMLHttpRequest) {
       // Mozilla, Safari...
   xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
      // IE
   try {
     
     xhr = new ActiveXObject('Msxml2.XMLHTTP');
   } catch (e) {
     
     try {
     
       xhr = new ActiveXObject('Microsoft.XMLHTTP');
     } catch (e) {
     }
   }
}
if (xhr) {
     
   xhr.onreadystatechange = onReadyStateChange;
   xhr.open('POST', '/api', true);
   // 设置 Content-Type 为 application/x-www-form-urlencoded
   // 以表单的形式传递数据
   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   xhr.send('username=admin&password=root');
}

// onreadystatechange 方法
function onReadyStateChange() {
     
   // 该函数会被调用四次
   console.log(xhr.readyState);
   if (xhr.readyState === 4) {
     
      // everything is good, the response is received
      if (xhr.status === 200) {
     
         console.log(xhr.responseText);
      } else {
     
         console.log('There was a problem with the request.');
      }
   } else {
     
      // still not ready
      console.log('still not ready...');
   }
}

从上边的代码可以看出,XMLHttpRequest 是一个非常粗糙的API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,前端程序员们不仅要做各个浏览器的兼容性,还饱受回调地狱的折磨,这显然不是一个好的选择。

jQuery实现AJAX
$.ajax({
     
    method: 'POST',
    url: '/api',
    data: {
      username: 'admin', password: 'root' }
})
  .done(function(msg) {
     
      alert( 'Data Saved: ' + msg );
  });

Query作为一个使用人数最多的库,其AJAX很好的封装了原生AJAX的代码,在兼容性和易用性方面都做了很大的提高,而且jQuery还把jsonp装在了AJAX里面,这样我们就可以开心的跨域了!!!!对比原生AJAX的实现,使用jQuery实现的AJAX就异常简单了.
但是,笔锋一转,我们仍然逃脱不了回调地狱。

Fetch
fetch(...).then(fun2)
          .then(fun3) //各依赖有序执行
          .....
          .catch(fun)

从上边的代码可以看出,fetch用起来想jQuery一样简单,虽然还是有Callback的影子,但是看起来舒服多了

2. 详解Fetch API

1. 兼容性

由于Fetch API是基于Promise设计,旧浏览器不支持Promise,需要使用pollyfill es6-promise

2. Fetch使用说明

由于Fetch API是基于Promise设计,旧浏览器不支持Promise,需要使用pollyfill es6-promise

fetch(url, options).then(function(response) {
      
// handle HTTP response
}, function(error) {
     
 // handle network error
})

a. fetch api返回的是一个promise对象
b. Options:

  • method(String): HTTP请求方法,默认为GET
  • body(String): HTTP的请求参数
  • headers(Object): HTTP的请求头,默认为{}
  • credentials(String): 默认为omit,忽略的意思,也就是不带cookie;还有两个参数,same-origin,意思就是同源请求带cookie;include,表示无论跨域还是同源请求都会带cookie

c. 第一个then函数里面处理的是response的格式

  • status(number): HTTP返回的状态码,范围在100-599之间
  • statusText(String): 服务器返回的状态文字描述
  • ok(Boolean): 如果状态码是以2开头的,则为true
  • headers: HTTP请求返回头
  • body: 返回体,这里有处理返回体的一些方法
    • text(): 将返回体处理成字符串类型
    • json(): 返回结果和 JSON.parse(responseText)一样
    • blob(): 返回一个Blob,Blob对象是一个不可更改的类文件的二进制数据
    • arrayBuffer()
    • formData()
3. Fetch常见坑
  1. 兼容性
    浏览器-Fetch_第1张图片
    IE浏览器完全不支持fetch,移动端的很多浏览器也不支持,所以,如果要在这些浏览器上使用Fetch,就必须使用fetch polyfill

  2. cookie传递
    必须在header参数里面加上credentials: 'include',才会如xhr一样将当前cookies带到请求中去

  3. fetch和 xhr、ajax 的不同
    fetch虽然底层,但是还是缺少一些常用xhr有的方法,比如能够取消请求(abort)方法

    当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject

    默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。自从2017年8月25日后,默认的credentials政策变更为same-originFirefox也在61.0b13中改变默认值

4. Fetch 参数

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

// Example POST method implementation:

postData('http://example.com/answer', {
     answer: 42})
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error))

function postData(url, data) {
     
  // Default options are marked with *
  return fetch(url, {
     
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
     
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // *client, no-referrer
  })
  .then(response => response.json()) // parses response to JSON
}
上传 JSON 数据
var url = 'https://example.com/profile';
var data = {
     username: 'example'};

fetch(url, {
     
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
     
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
上传文件
var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
     
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
检测请求是否成功
fetch('flowers.jpg').then(function(response) {
     
  if(response.ok) {
     
    return response.blob();
  }
  throw new Error('Network response was not ok.');
}).then(function(myBlob) {
      
  var objectURL = URL.createObjectURL(myBlob); 
  myImage.src = objectURL; 
}).catch(function(error) {
     
  console.log('There has been a problem with your fetch operation: ', error.message);
});
自定义请求对象
var myHeaders = new Headers();

var myInit = {
      method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

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

fetch(myRequest).then(function(response) {
     
  return response.blob();
}).then(function(myBlob) {
     
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});
Headers
myHeaders = new Headers({
     
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});

如果使用了一个不合法的HTTP Header属性名,那么Headers的方法通常都抛出 TypeError 异常。如果不小心写入了一个不可写的属性,也会抛出一个 TypeError 异常。除此以外的情况,失败了并不抛出异常。例如:

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

不管是请求还是响应都能够包含body对象. body也可以是以下任意类型的实例

  • ArrayBuffer
  • ArrayBufferView (Uint8Array等)
  • Blob/File
  • string
  • URLSearchParams
  • FormData

Body 类定义了以下方法 (这些方法都被 Request 和Response所实现)以获取body内容. 这些方法都会返回一个被解析后的Promise对象和数据

  • arrayBuffer()
  • blob()
  • json()
  • text()
  • formData()

更多内容可以在 Fetch API 上看

比起XHR来,这些方法让非文本化的数据使用起来更加简单

request和response(包括fetch() 方法)都会试着自动设置Content-Type。如果没有设置Content-Type值,发送的请求也会自动设值

参考链接
  1. 浅谈fetch
  2. Fetch API
  3. JavaScript使用 Fetch

你可能感兴趣的:(浏览器)