xhr、ajax、axios、fetch

712 阅读7分钟

XMLHttpRequest

XMLHttpRequest优点

1.不重新加载页面的情况下更新网页 2.在页面已加载后从服务器请求/接收数据 3.在后台向服务器发送数据。

XMLHttpRequest缺点

1.使用起来也比较繁琐,需要设置很多值。 2.早期的IE浏览器有自己的实现,这样需要写兼容代码 兼容性查询:caniuse.com/?search=XML…

一、版本区别

XMLHttpRequest Level 1缺点

  1. 不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据。
  2. 在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成。
  3. 受同源策略的限制,不能发送跨域请求。

XMLHttpRequest Level 2新增功能

  1. 在服务端允许的情况下,可以发送跨域请求。
  2. 支持发送和接收二进制数据。
  3. 新增formData对象,支持发送表单数据。
  4. 发送和获取数据时,可以获取进度信息。
  5. 可以设置请求的超时时间

二、请求API

1.open()方法:用于初始化一个请求,接收三个参数。

  • 第一个参数 method:要发送的请求的类型。比如GET、POST、PUT、DELETE等。
  • 第二个参数 url:请求的URL。
  • 第三个参数 async:是否异步发送请求的布尔值。true为异步发送请求。

const xhr = new XMLHttpRequest() xhr.open('get', '/userInfo', true)

2.send()方法:发送HTTP请求,接收一个参数data。

data:作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null。该参数可以接收字符串、FormData、Blob、ArrayBuffer等类型。

const xhr = new XMLHttpRequest() xhr.send(null)

3. setRequestHeader()方法:可以设置自定义的请求头部信息,接收两个参数header:头部字段的名称;value:头部字段的值。

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
// xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  
xhr.setRequestHeader('MyHeader', 'MyValue')
xmlhttp.send()

注:要成功发送请求头部信息,此方法必须在open()和send()之间调用

4.readyState属性和onreadystatechange事件

readyState表示请求/响应过程的当前活动阶段。属性值如下:

  • 0(UNSENT)未初始化。尚未调用open()方法。
  • 1(OPENED)启动。已经调用open()方法,但没有调用send()方法。
  • 2(HEADERS_RECEIVED)发送。已经调用send()方法,但尚未接收到响应。
  • 3(LOADING)接收。已经接收到部分响应数据。
  • 4(DONE)完成。已经接收到全部响应数据。

只要readyState属性的值发生变化,都会触发一次onreadystatechange事件。利用这个事件来检测每次状态变化后readyState的值。一般情况下只对readyState值为4的阶段做处理,此时所有数据都已经就绪。

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.onreadystatechange = function () {
  if(xhr.readyState !== 4) {
    return
  }
  if(xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText)
  }
}
xhr.send(null)

5.timeout属性和ontimeout事件

Timeout:表示请求在等待响应多少毫秒之后就终止。如果在规定的时间内浏览器还没有接收到响应,就会触发ontimeout事件处理程序。

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
//将超时设置为3秒钟
xhr.timeout = 3000 
// 请求超时后请求自动终止,会调用 ontimeout 事件处理程序
xhr.ontimeout = function(){
    console.log('请求超时了')
}
xhr.send(null)

6.overrideMimeType()方法:能够重写服务器返回的MIME类型,从而让浏览器进行不一样的处理

假如服务器返回的数据类型是text/xml,由于种种原因浏览器解析不成功报错,这时就拿不到数据。为了拿到原始数据,我们可以把MIME类型改成text/plain,这样浏览器就不会去自动解析,从而我们就可以拿到原始文本了。

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.overrideMimeType('text/plain')
xhr.send(null)

7.responseType属性:是一个字符串,告诉服务器返回指定类型的数据。使用xhr.response属性来接收。

调用在open()方法之后,send()方法之前

当将responseType设置为一个特定的类型时,你需要确保服务器所返回的类型和你所设置的返回值类型是兼容的。那么如果两者类型不兼容,服务器返回的数据就会变成null,即使服务器返回了数据。

//获取图片responseType抛出InvalidAccessError的异常示例。
const xhr = new XMLHttpRequest()
xhr.open('get', '/server/image.png', true)
xhr.responseType = 'blob' // 正确使用arraybuffer
xhr.onload = function(e) {
  if (xhr.status >= 200 && xhr.status < 300) {
    const blob = this.response
    // ...
  }
}
xhr.send(null)

8.withCredentials属性:是一个布尔值,表示跨域请求时是否协带凭据信息(cookie、HTTP认证及客户端SSL证明等)。默认为false。

如果跨域Ajax请求发送Cookie,withCredentials属性设为true。同域下写不写都一样

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xhr.withCredentials = true
xhr.send(null)

当配置了withCredentials为true时,须在后端增加response头信息Access-Control-Allow-Origin,且必须指定域名,而不能指定为*。并且添加Access-Control-Allow-Credentials这个头信息为true。

response.addHeader("Access-Control-Allow-Origin", "http://example.com")
response.addHeader("Access-Control-Allow-Credentials", "true")

9.abort()方法和onabort事件

在接收到响应之前调用abort()方法用来取消异步请求。当一个请求被终止后,它的readyState属性将被置为0。在终止请求之后,还应该对XMLHttpRequeat对象进行解引用操作。 当调用abort()后,会触发onabort事件。

const xhr = new XMLHttpRequest()
xhr.open('get', '/server', true)
xmlhttp.onabort = function () {
  console.log('请求被中止')
}
xmlhttp.send()
// 将会调用我们上面定义的 onabort 回调函数
xmlhttp.abort()

10.GET请求

将查询字符串参数追加到URL的末尾,将信息发送给服务器。 GET参数的编码方式是无法人为干涉的,这导致了不同浏览器有不同的编码方式,因此最稳妥的方案是人工预编码,人工解码,从而禁止浏览器编码的干涉。

// 使用encodeURIComponent()进行编码
const tempParam = encodeURIComponent('age')
const tempValue = encodeURIComponent('20')
xhr.open('get', '/server?tempParam=tempValue&money=100', true)

11.POST请求:把数据作为请求的主体(请求的body)提交。

常见的POST请求提交数据方式。

    1. application/x-www-form-urlencoded 浏览器的原生form表单,如果不设置enctype属性,那么最终就会以application/x-www-form-urlencoded方式提交数据。
    1. multipart/form-data 表单上传文件时,必须让form表单的enctype等于multipart/form-data。
    1. application/json 当发送Ajax请求时,把application/json作为请求头,用来告诉服务端消息主体是序列化后的JSON字符串。
    1. text/xml
  • 使用HTTP作为传输协议,XML作为编码方式的远程调用规范
  • 使用XMLHttpRequest模拟表单提交
  • 将Content-Type头部信息设置为application/x-www-form-urlencoded
    1. XMLHttpRequest模仿表单提交。
const xhr = new XMLHttpRequest()
 xhr.open('post', '/server', true)
 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
 const form = document.getElementById('myForm') 
 // serialize()为表单序列化方法
 xhr.send(serialize(form))
  • 6.XMLHttpRequest level 2的FormData来序列化表单数据。
 const xhr = new XMLHttpRequest()
 xhr.open('post', '/server', true)
 const form = document.getElementById('myForm')
 const formData = new FormData(form)
 formData.append("id", "123456")
 xhr.send(formData)

使用FormData不用设置请求头部。XMLHttpRequest对象能够识别传入的数据类型是FormData的实例,并配置适当的头部信息

三、XMLHttpRequest进度事件相关API

1.onloadstart

在XMLHttpRequest对象开始传送数据时被触发,也就是调用send()方法(HTTP 请求发出)的时候。

 xhr.onloadstart = function () {
    console.log('开始发出请求...')
 }

2.onprogress

在接收响应期间持续不断地触发;会接收到一个event对象,它的target属性是XMLHttpRequest对象,并且event包含着三个额外的属性:loaded、total和lengthComputable。

  • event.loaded:已传输的数据量(已经接收的字节数)。
  • event.total:总共的数据量(根据Content-Length响应头部确定的预期字节数)。
  • event.lengthComputable:进度信息是否可用的布尔值。

有了这些信息,就可以创建一个Ajax请求进度条了。

const xhr = new XMLHttpRequest()
xhr.onprogress = function (event) {
    if (!event.lengthComputable) {
        return console.log('无法计算进展')
    }
    const percentComplete = event.loaded / event.total * 100
    console.log(`进度百分比:${percentComplete}%`)
}
xhr.open('post', '/server', true)
xhr.send(null)

3.onerror

在请求发生错误时触发。只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,比如响应返回的statusCode是4xx时,并不属于NetWork Error,所以不会触发onerror事件,而是会触发onload事件。

xhr.onerror = function(e) {
 console.log('数据接收出错')
}

4.onabort

调用abort()方法而终止请求时触发。

5.onload

当请求成功,接收到完整的响应数据时触发,可以用来替代readystatechange事件;只要浏览器接收到服务器的响应,不管其状态如何,都会触发load事件。

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
  console.log('数据接收完毕')
  if(xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText)
  }
}
xhr.open('post', '/server', true)
xhr.send(formData)

为确保正常执行,必须在调用open()方法之前添加onprogress事件处理程序。

6.onloadend

在请求结束(包括请求成功和请求失败),或者触发error、abort或load事件后触发。

xhr.onloadend = function(e) {
  console.log('请求结束,状态未知')
}

每个请求都从触发loadstart事件开始,接下来是一或多个progress事件,然后触发error、 abort或load事件中的一个,最后以触发loadend事件结束。

7.upload

XMLHttpRequest不仅可以发送请求,还可以发送文件,就是Ajax文件上传。 发送文件以后,通过XMLHttpRequest.upload属性可以得到一个XMLHttpRequestUpload对象。通过这个对象,可以得知上传的进展。实现方案就是监听这个对象的各种事件:onloadstart、onprogress、onabort、onerror、onload、ontimeout、onloadend。 当文件上传时,对upload属性指定progress事件的监听函数,可获得上传的进度。

const xhr = new XMLHttpRequest()
if (xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
        if (e.total > 0) {
            e.percent = e.loaded / e.total * 100
        }
    }
}

四、XMLHttpRequest对象接收响应相关API

1. 响应头相关

  • Content-Type:服务器告诉客户端响应内容的类型和采用字符编码。比如:Content-Type: text/html; charset=utf-8。
  • Content-Length:服务器告诉客户端响应实体的大小。比如:Content-Length: 8368。
  • Content-Encoding:服务器告诉客户端返回的的压缩编码格式。比如:Content-Encoding: gzip, deflate, br。 2.status

返回一个整数,表示服务器回应的HTTP状态码。如果服务器没有返回状态码,那么这个属性默认是200。请求发出之前,该属性为0。该属性只读**。

if (xhr.readyState === 4) {
  if (xhr.status >= 200 && xhr.status < 300) {
    // 处理服务器的返回数据
  }
}

3.statusText

返回一个只读属性字符串,表示服务器发送的状态说明。比如OK和Not Found。在请求发送之前,该属性的值是空字符串。如果服务器没有返回状态提示,该属性的值默认为OK。**

要通过检测status属性来决定下一步的操作,不要依赖statusText,因为statusText在跨浏览器使用时不太可靠。

4.response

表示服务器返回的只读数据。它可以是任何数据类型,比如字符串、对象、二进制对象等,具体的类型由XMLHttpRequest.responseType属性决定。**

如果本次请求没有成功或者数据不完整,该属性等于null。

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    console.log(xhr.response)
  }
}

5.responseText

返回从服务器接收到的字符串,该属性为只读。**

if (xhr.readyState === 4) {
  if (xhr.status >= 200 && xhr.status < 300) {
    // 处理服务器的返回数据
    console.log(xhr.responseText)
  }
}

6.responseXML属性

如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着响应数据的HTML或XML文档对象。该属性是只读属性。

无论内容类型是什么,响应主体的内容都会保存到responseText属性中。而对于非XML数据而言,responseXML属性的值将为null。

7.responseURL属性

是字符串,表示发送数据的服务器的网址。如果URL为空则返回空字符串。如果URL有锚点,则位于URL#后面的内容会被删除。如果服务器端发生跳转,这个属性返回最后实际返回数据的网址。

const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://example.com/test', true)
xhr.onload = function () {
  // 返回 http://example.com/test
  console.log(xhr.responseURL)
}
xhr.send(null)

8.getResponseHeader()方法

返回HTTP头信息指定字段的值,如果还没有收到服务器的响应或者指定字段不存在,返回null。该方法的参数不区分大小写。

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
   console.log(xhr.getResponseHeader('Content-Type'))
}
xhr.open('post', '/server', true)
xhr.send(null)

如果有多个字段同名,它们的值会被连接为一个字符串,每个字段之间使用逗号+空格分隔。

9.getAllResponseHeaders()方法

返回一个字符串,表示服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分隔(回车+换行),如果没有收到服务器回应,该属性为null。如果发生网络错误,该属性为空字符串。

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
 const responseHeaders = 'getAllResponseHeaders' in xhr ? xhr.getResponseHeaders() : null
}
xhr.open('post', '/server', true)
xhr.send(null)

Ajax

兼容性查询:caniuse.com/?search=AJA…

1.创建Ajax核心对象XMLHttpRequest

var xhr=null;  
  if (window.XMLHttpRequest)  
    {// 兼容 IE7+, Firefox, Chrome, Opera, Safari  
    xhr=new XMLHttpRequest();  
    } else{// 兼容 IE6, IE5 
      xhr=new ActiveXObject("Microsoft.XMLHTTP");  
    }

2.向服务器发送请求

xhr.open(method,url,async);  // 初始化一个请求,同XHR
send(string);//post请求时才使用字符串参数,否则不用带参数。 
//post请求要设置请求头的格式内容
 xhr.open("POST","test.html",true);  
 xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  
 xhr.send("fname=Henry&lname=Ford"); 

3.服务器响应处理

responseText:获得字符串形式的响应数据。
responseXML:获得XML 形式的响应数据。
  • 同步处理
 xhr.open("GET","info.txt",false);  
 xhr.send();  
  //获取数据直接显示在页面上
 document.getElementById("myDiv").innerHTML=xhr.responseText; 
  • 异步处理
 xhr.onreadystatechange=function()  { 
    if (xhr.readyState==4 &&xhr.status==200)  { 
       document.getElementById("myDiv").innerHTML=xhr.responseText;  
    }
}

axios

axios基于promise用于浏览器和nodejs的http客户端,本质上也是对原生XHR的封装,只不过他是promise的实现版本,符合最新ES规范

兼容性查询:caniuse.com/?search=AJA…

axios优点

  1. 支持浏览器与nodejs前后端发请求
  2. 支持promise语法,
  3. 支持自动解析json
  4. 支持中断请求
  5. 支持拦截请求
  6. 支持进度条检测
  7. 支持客户端防止CSRF
  8. 提供一些并发请求的接口(重要,方便了很多的操作)

axios缺点

  • 低版本浏览器无法使用promise,需要添加polyfill

axios API

axios(config)

//发起 POST请求
axios({
    method:'post',//方法
    url:'/user/12345',//地址
    data:{//参数
        firstName:'Fred',
        lastName:'Flintstone'
    }
});

//通过请求获取远程图片
axios({
    method:'get',
    url:'http://bit.ly/2mTM3Ny',
    responseType:'stream'
}).then(function(response){
    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
})

axios(url[,config])

//发起一个GET请求 
axios('/user/12345/);

请求方法的别名

  • axios.request(config)
  • axios.get(url[,config])
  • axios.delete(url[,config])
  • axios.head(url[,config])
  • axios.options(url[,config])
  • axios.post(url[,data[,config]])
  • axios.put(url[,data[,config]])
  • axios.patch(url[,data[,config]])

并发 Concurrency

  • axios.all(iterable):类似Promise.all;接受一个数组作为参数,数组中的每个元素都是一个请求,返回一个promise对象,当数组中所有请求均已完成时,执行then方法。
  • axios.spread(callback):接收一个函数作为参数,返回一个新的函数
function getUserAuth(){
    return axios.get('/auth/xxxx');
}

function getUserMenu(){
    return axios.get('/auth/xxxx/permissions');
}

axios.all([getUserAuth(), getUserMenu()])
    .then(axios.spread(function(acc,pers){
        console.log('两个请求已完成')
    })
);

Requeset Config请求设置

1.url: 必填 用于请求的服务器URL

url: '/Auth/xxxxx',

2.baseURL: 非必填 如果url不是绝对地址,那么将会加在其前面 可对相对地址的axios实例设置baseUrl

baseURL: 'https://xxx.com/api/',

3.proxy:非必填 定义代理服务器的主机名、端口、auth

  • auth定义HTTP应该跟proxy连接的基本认证,并且提供证书
  • 将设置一个Proxy-Authorization头(header),覆盖原先自定义
 proxy: {
   host: '127.0.0.1',
   port: 9000,
   auth: {username: 'cdd', password: '123456'}
 },

4.method: 非必填 发起请求时的方法 默认get

method: 'get',

**5.headers:非必填 自定义的要被发送的信息头 **

headers: {'X-Requested-with': 'XMLHttpRequest'},

6.params:请求参数,必须是一个纯对象,或者URLSearchParams对象

params: { ID: 12345 },

7.data:非必填 请求主体发送的数据对象

  • 适用于发送POST、PUT或者PATCH请求的数据对象
  • 当没设置transformRequest时,必须是以下类型之一: string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  • Browser only(限浏览器): FormData, File, Blob
  • Node only(限Node): Stream

data: { firstName: 'fred' },

8.timeout:非必填 指定请求超时之前的毫秒数

timeout: 1000,

9.withCredentials: 非必填 设置跨跨域请求书否需要证明

withCredentials: false, //默认值

10.auth:非必填 提供凭证用于完成http的身份验证

将会在headers中设置一个Authorization授权信息。自定义Authorization授权要设置在headers中

 auth: {
   username: '',
   password: ''
 },

11.responseType:非必填 服务器返回的数据类型

// arraybuffer、blob、document、json、text、stream
responseType: 'json', 

12.responseEncoding:非必填 解析相应的编码方式

:会忽视responseType为stream或者客户端的请求。 responseEncoding: 'utf8', //默认值

13.onUploadProgress:处理上传过程的进程事件

onUploadProgress: function (progressEvent) { //本地过程事件发生时想做的事 },

14.onDownloadProgress:处理下载过程的进程事件

onDownloadProgress: function (progressEvent) { //下载过程中想做的事 },

15.maxContentLength:非必填 定义接收到的response响应数据的最大字节容量

maxContentLength: 2000,

16.adapter:适配器 允许自定义处理请求,返回一个promise和一个有效的response

adapter: function (config) {
   /*...*/
 },

17.httpAgent:非必填 定义在nodejs用http请求时的代理 允许配置类似keepAlive的选项

httpAgent: new http.Agent({keepAlive: true}),

18.httpsAgent:非必填 定义在nodejs用https请求时的代理 允许配置类似keepAlive的选项

httpsAgent: new https.Agent({keepAlive: true}),

19.transformRequest:非必填 允许请求的数据在发送至服务器之前进行转化

  • 只适用于PUT,POST,PATCH方法,并且必须返回一个string、ArrayBuffer或Stream象
transformRequest: [function (data, headers) {
   //依自己的需求对请求数据进行处理
   return data;
 }],

20.transformResponse:非必填 允许对返回的数据传入then/catch之前进行处理

transformResponse: [function (data) {
   //依需要对数据进行处理
   return data;
 }],

21.paramsSerializer:非必填 是params参数序列化器

paramsSerializer: function (params) {
   return Qs.stringify(params, {arrayFormat: 'brackets'})
},

21.xsrfCookieName:非必填 设置xsrf token 值的 cookie 名称

xsrfCookieName: 'XSRF-TOKEN',//默认值

23.xsrfHeaderName:非必填 设置xsrf token 值的 http head 名称

xsrfHeaderName: 'X-XSRF-TOKEN',//默认值

24.validateStatus:非必填 设置是否解析或拒绝给定的promise

  • HTTP响应状态码,若返回true,promise将被解析;若返回false,promise将被拒绝。
  • 若设置为null、undefined, promise将被解析
validateStatus: function (status) {
   return status >= 200 && stauts < 300;//默认
 },

25.maxRedirects:非必填 在node.js中redirect的最大值 为0时没有redirect

maxRedirects: 5,//默认值

26.socketPath:非必填 数据转发

  • 在node.js中使用的UNIX Socket
  • socketPath和proxy二选一,若都定义,则使用socketPath

socketPath: null,//默认值

27.cancelToken:非必填 用于取消请求

cancelToken: new CancelToken(function (cancel) {})

Response Schema

  • data:服务器的提供的回复(相对于请求) data{},
  • status:服务器返回的http状态码 status:200,
  • statusText:服务器返回的http状态信息 statusText: 'ok',
  • headers:服务器返回中携带的headers headers:{},
  • config:对axios进行的设置,目的是为了请求(request) config:{}
  • request:获取当前相应的请求
    • 是node.js中最后一次的ClientRequest的实例(在redirect中)
    • 或者是在浏览器中的XMLHttpREquest实例

axios无感刷新token

在响应拦截器中拦截,判断token 返回过期后,调用刷新token接口

import axios from 'axios'
// 拦截器
axios.interceptors.response.use(
  response => {
    if (response.data.code === 409) {
      if (!isRefreshing) {
        isRefreshing = true
        return refreshToken({ 
           refreshToken: localStorage.getItem('refreshToken'),
           token: getToken() 
        }).then(res => {
          const { token } = res.data
          setToken(token)
          response.headers.Authorization = `${token}`
        }).catch(err => {
          removeToken()
          router.push('/login')
          return Promise.reject(err)
        }).finally(() => {
          isRefreshing = false
        })
      }
    }
    return response && response.data
  },
  (error) => {
    Message.error(error.response.data.msg)
    return Promise.reject(error)
  }
)

Fetch

Fetch是基于promise设计的,是原生js API,没有使用XMLHttpRequest对象,返回一个promise对象

兼容性查询:caniuse.com/?search=fet…

Fetch优点

  1. 语法简洁,更加语义化
  2. 基于标准 Promise 实现,支持 async/await
  3. 同构方便,更加底层,提供的API丰富(request, response, body , headers)
  4. 脱离了XHR,是ES规范里新的实现方式

Fetch缺点

  • fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject。
  • fetch默认不会带cookie,需要添加配置项: fetch(url,{credentials:'include'})。
  • fetch不支持abort,不支持超时控制,使用 setTimeout 及promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费。
  • fetch没有办法原生监测请求的进度,上传文件时,无法进度检测,而XHR可以

fetchAPI

  • URL:请求地址
  • Config:配置对象,定制发出的 HTTP 请求。
fetch(url, Config)

Requeset Config请求设置

1.mode

指定请求的模式,

  • cors:默认值,允许跨域请求。
  • same-origin:只允许同源请求。
  • no-cors:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求。

2.credentials

指定是否发送 Cookie,跨域请求发送 Cookie,需设为include

  • same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送。
  • include:不管同源请求,还是跨域请求,一律发送 Cookie。
  • omit:一律不发送

3.method

HTTP 请求的方法,POSTDELETEPUT都在这个属性设置。

4.headers

一个对象,用来定制 HTTP 请求的标头。

5.body

post请求的数据体。

  • 默认方式
const response = await fetch(url, {
  method: 'POST',
  headers: {
    "Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
  },
  body: 'foo=bar&lorem=ipsum',
});
const json = await response.json();
  • json方式
const user =  { name:  'John', surname:  'Smith'  };
const response = await fetch('/article/fetch/post/user', {
  method: 'POST',
  headers: {
   'Content-Type': 'application/json;charset=utf-8'
  }, 
  body: JSON.stringify(user) 
});
  • 表单方式
const form = document.querySelector('form');

const response = await fetch('/users', {
  method: 'POST',
  body: new FormData(form)
})
  • 文件上传方式
const input = document.querySelector('input[type="file"]');

const data = new FormData();
data.append('file', input.files[0]);
data.append('user', 'foo');

fetch('/avatars', {
  method: 'POST',
  body: data
});
  • 二进制方式
let blob = await new Promise(resolve =>   
  canvasElem.toBlob(resolve,  'image/png')
);

let response = await fetch('/article/fetch/post/image', {
  method:  'POST',
  body: blob
})

6.cache

指定如何处理缓存

  • default:默认值,先在缓存里面寻找匹配的请求。
  • no-store:直接请求远程服务器,并且不更新缓存。
  • reload:直接请求远程服务器,并且更新缓存。
  • no-cache:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存。
  • force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器。
  • only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误。

7.referrer

用于设定fetch()请求的referer标头,字符串类型,空字符串(表示部分发送referer标头。

fetch('/page', {
  referrer: ''
});

8.referrerPolicy

  • no-referrer-when-downgrade:默认值,总是发送Referer标头,除非从 HTTPS 页面请求 HTTP 资源时不发送。
  • no-referrer:不发送Referer标头。
  • originReferer标头只包含域名,不包含完整的路径。
  • origin-when-cross-origin:同源请求Referer标头包含完整的路径,跨域请求只包含域名。
  • same-origin:跨域请求不发送Referer,同源请求发送。
  • strict-originReferer标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer标头。
  • strict-origin-when-cross-origin:同源请求时Referer标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头。
  • unsafe-url:不管什么情况,总是发送Referer标头。

9.redirect

指定 HTTP 跳转的处理方法

  • follow:默认值,跟随 HTTP 跳转。
  • error:如果发生跳转,fetch()就报错。
  • manual:不跟随 HTTP 跳转,但是response.url属性会指向新的 URL,response.redirected属性会变为true,由开发者自己决定后续如何处理跳转。

10.integrity

指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值。

// 检查下载文件的 SHA-256 哈希值是否相符,确保没有被篡改
fetch('http://site.com/file', {
  integrity: 'sha256-abcdef'
});

11.keepalive

当页面卸载时,浏览器在后台是否保持连接,继续发送数据

window.onunload = function() {
  fetch('/analytics', {
    method: 'POST',
    body: "statistics",
    keepalive: true // 默认 false
  });
};

12.signal

指定一个 AbortSignal 实例,用于取消请求

//新建实例
let controller = new AbortController(); 
let signal = controller.signal;
//发送请求
fetch(url, {
  signal: controller.signal
});
// 监听abort
signal.addEventListener('abort',
  () => console.log('abort!')
);
controller.abort(); // 取消
console.log(signal.aborted); // true

Responses属性及方法

1.ok

返回一个布尔值,表示请求是否成功 true对应 HTTP 请求的状态码 200 到 299;false对应其他的状态码。

2.status

返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。

3.statusText

返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回"OK")。

4.url

返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。

5.type

返回请求的类型。可能的值如下:

  • basic:普通请求,即同源请求。
  • cors:跨域请求。
  • error:网络错误,主要用于 Service Worker。
  • opaque:如果fetch()请求的type属性设为no-cors,就会返回这个值,详见请求部分。表示发出的是简单的跨域请求,类似<form>表单的那种跨域请求。

6.opaqueredirect

如果fetch()请求的redirect属性设为manual,就会返回这个值,详见请求部分。

7.body

Response 对象暴露出的底层接口,返回一个 ReadableStream 对象,供用户操作。

它可以用来分块读取内容,应用之一就是显示下载的进度。

const response = await fetch('flower.jpg');
const reader = response.body.getReader();

while(true) {
  const {done, value} = await reader.read();

  if (done) {
    break;
  }

  console.log(`Received ${value.length} bytes`)
}

上面示例中,response.body.getReader()方法返回一个遍历器。这个遍历器的read()方法每次返回一个对象,表示本次读取的内容块。

这个对象的done属性是一个布尔值,用来判断有没有读完;value属性是一个 arrayBuffer 数组,表示内容块的内容,而value.length属性是当前块的大小。

8.text()

用于获取文本数据,比如 HTML 文件。

const response = await fetch('/users.html');
const body = await response.text();
document.body.innerHTML = body

9.json()

于获取服务器返回的 JSON 数据。

fetch('https://api.github.com/users/ruanyf')
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(err => console.log('Request Failed', err)); 

10.formData()

主要用在 Service Worker 里面,拦截用户提交的表单,修改某些数据以后,再提交给服务器。

11.blob()

用于获取二进制文件。

const response = await fetch('flower.jpg');
const myBlob = await response.blob();
const objectURL = URL.createObjectURL(myBlob);
const myImage = document.querySelector('img');
myImage.src = objectURL; // 显示在网页上

12.arrayBuffer()

用于获取流媒体文件。

 // 获取音频文件`song.ogg`后在线播放
 const audioCtx = new window.AudioContext();
 const source = audioCtx.createBufferSource();

 const response = await fetch('song.ogg');
 const buffer = await response.arrayBuffer();

 const decodeData = await audioCtx.decodeAudioData(buffer);
 source.buffer = buffer;
 source.connect(audioCtx.destination);
 source.loop = true;

13.clone()

创建Response对象的副本,实现多次读取。

//由于Stream 对象只能读取一次,再次读取会报错
let text =  await response.text(); // 第一次读取
let json =  await response.json(); // 再次读取 报错

正确用法

const response1 = await fetch('flowers.jpg');
const response2 = response1.clone();

const myBlob1 = await response1.blob();
const myBlob2 = await response2.blob();

image1.src = URL.createObjectURL(myBlob1);
image2.src = URL.createObjectURL(myBlob2);

14.redirect()

将 Response 结果重定向到指定的 URL。该方法一般只用在 Service Worker 里面

fetch 并发请求

function handleFetchQueue(urls, max, callback) {
  const urlCount = urls.length;
  const requestsQueue = [];
  const results = [];
  let i = 0;
  const handleRequest = (url) => {
    const req = fetch(url).then(res => {
      console.log('当前并发: '+requestsQueue);
      const len = results.push(res);
      if (len < urlCount && i + 1 < urlCount) {
        requestsQueue.shift();
        handleRequest(urls[++i])
      } else if (len === urlCount) {
        ('function' === typeof callback) && callback(results)
      }
    }).catch(e => {
      results.push(e)
    });
    if (requestsQueue.push(req) < max) {
      handleRequest(urls[++i])
    }
  };
  handleRequest(urls[i])
}

const urls = Array.from({length: 10}, (v, k) => k);
const fetch = function (idx) {
  return new Promise(resolve => {
    console.log(`start request ${idx}`);
    const timeout = parseInt(Math.random() * 1e4);
    setTimeout(() => {
      console.log(`end request ${idx}`);
      resolve(idx)
    }, timeout)
  })
};

const max = 4;
const callback = () => {
  console.log('run callback');
};
handleFetchQueue(urls, max, callback);