【web通信】Ajax与Fetch

怼怼

Ajax和Fecth

两者都用于无刷新更新数据:即在不刷新页面的情况下,向服务端发送请求获取数据

AJAX(Asynchronous JavaScript+XML):异步JavaScript加XML

  • 关键是使用XMLHttpRequest(XHR)对象

FetchFetch API提供了一个原生JavaScript接口,用于访问和操纵HTTP管道的一些具体部分

  • Fetch API可以实现XHR对象的所有任务
  • 并且更容易使用,接口也更现代化
原生开发建议使用Fetch,Vue项目使用axios
axios后续结合Vue介绍

1 XMLHttpRequest对象

  1. 概述
  2. XHR的属性、方法、事件
  3. XHR的使用与封装

1.1 概述

  • 起初XHR对象是通过ActiveX对象实现并包含在MSXML库中
  • 现在的浏览器都通过XMLHttpRequest构造函数原生支持XHR对象(低版本IE除外)

    //IE
    var xhr1 = new ActiveXObjet('Microsoft.XMLHTTP')
    //主流浏览器
    cosnt xhr2 = new XMLHttpRequest()
    console.log(xhr2)

    原型上的一些属性与方法
    image.png

1.2 XHR的属性、方法、事件

  • 属性

    属性描述只读
    readyState请求状态1
    onreadystatechange状态改变时调用0
    response响应实体1
    responseType定义响应类型0
    responseText文本数据1
    responseURL响应URL1
    responseXML可解析为XML的响应1
    status状态码1
    statusText完整响应文本1
    timeout最大请求时间0
    ontimeout请求超时时调用0
    withCredentials布尔值,跨域请求是否可带授权信息0
  • 方法

    方法描述
    open(methond,url,async,user,password)初始化请求
    send(body)发送请求
    abort()如果请求发出,终止请求
    overrideMimeType(mimeType)覆盖服务器返回的MIME类型
    setReuquestHeader(header,value)设置HTTP请求头部,在open与send之间使用
  • 事件

    事件描述
    loadstart接收到响应数据时触发
    progress请求接收到更多数据时,接收响应期间反复触发
    error请求出错触发
    abort请求被停止时触发
    load请求成功时触发
    loadend请求完成触发,不管成功失败
    timeout在预设时间内没有收到响应时触发
  • readState属性

    状态描述
    0UNSENT代理被创建,未调用open()
    1OPENEDopen()被调用,send()未调用
    2HEADERS_RECEIVEDsend被调用,头部与状态可获得
    3LOADING下载中,response可获得
    4DONE下载操作完成

1.3 XHR的使用与封装

1.3.1 基本使用

<!-- html -->
<button id="req">请求</button>
const btn = document.getElementById('req')

const xhr = new XMLHttpRequest()
btn.addEventListener('click', () => {
    xhr.addEventListener('readystatechange', () => {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                console.log(xhr.response);
            } else {
                console.log(xhr.readyState, xhr.status);
            }
        }
    })
    xhr.open('GET', 'http://localhost:8000', true)
    xhr.send()
    xhr.responseType = 'json'
})
当需要在请求成功后再进行其他请求,结构会很臃肿,需要进一步的封装
ajax请求跨域问题这里使用的是CORS(Cross-Origin Resource Sharing 跨源资源共享)方式解决,在服务端的响应头部添加相关字段

1.3.2 封装

回调封装

  • 封装

    function ajaxCallback(url, fnSucc, fnFail) {
      const xhr = new XMLHttpRequest()
      xhr.addEventListener('readystatechange', () => {
          if (xhr.readyState === 4) {
              if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                  fnSucc(xhr.responseText)
              } else if (fnFail) {
                  fnFail(xhr.status)
              } else {
                  console.log('请求出错');
              }
          }
      })
      xhr.open('GET', url, true)
      xhr.send()
    }
  • 使用

    btn.addEventListener('click', () => {
      ajaxCallback('http://localhost:8000', res => {
          console.log(res);
          ajaxCallback('http://localhost:8085', res => {
              console.log(res);
          })
      })
    })
    这种方式又会出现一个经典的问题-回调地狱,下面我们使用Promise再次封装

Promise封装

  • 封装

    function ajaxPromise(url) {
      return new Promise((resolve, reject) => {
          const xhr = new XMLHttpRequest()
          xhr.addEventListener('readystatechange', () => {
              if (xhr.readyState === 4) {
                  if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                      resolve(xhr.responseText)
                  } else {
                      reject(xhr.status)
                  }
              }
          })
          xhr.addEventListener('error', () => {
              reject('请求出错');
          })
          xhr.open('GET', url, true)
          xhr.send()
      })
    }
  • 使用

    btn.addEventListener('click', () => {
      ajaxPromise('http://localhost:8000')
          .then(val => {
              console.log(val);
              return ajaxPromise('http://localhost:8085')
          })
          .then(val => {
              console.log(val);
          })
          .catch(res => {
              console.log(res);
          })
    })
    这种链式调用方式可以避免回调地狱,但有时其执行顺序不太容易理解

2 Fetch API

Fetch API除了请求数据外,也可以在服务端使用,提供拦截、重定向和修改通过fetch()生成的请求

  • fetch()方法返回Promise,不需要再进行Promise封装
  1. fetch()方法与基本使用
  2. Headers对象
  3. Request对象
  4. Reponse对象
  5. Body混入

2.1 fetch()方法与基本使用

  1. 使用fetch()方法
  2. fetch()自定义配置

2.1.1 使用fetch()方法

    fetch('http://localhost:8000')
        .then(response => response.text())
        .then(data => {
            console.log(data);
        })
  • fetch的第一个参数是url,执行返回一个Promise
  • 第一个then返回Response的实例化对象,调用其text()方法也会返回一个Promise
  • 第二个then可以接收到实际的数据

    fetch从执行到获取到数据实际上进行了两层的Promise链式执行,并不是直接获得数据

2.1.2 fetch()自定义配置

在只有一个url参数时进行的是默认的操作,方法使GET,fetch提供了第二个参数,以对象形式进行自定义配置

选项描述
body指定请求体的内容Blob、BufferSource、FormData、URLSearchParams、ReadableStream、String的实例
cache控制浏览器与HTTP缓存的交互下述
credentials指定请求中如何包含cookie下述
headers指定请求头Headers对象实例
integrity强制资源完整包含子资源完整性标识符的字符串
keepalive允许请求存在时间超出页面生命周期默认false
method请求方法默认GET
mode决定跨域请求的响应是否有效下述
redirect如何处理重定向响应下述
referrer指定HRRP的Referrer头部内容下述
referrerPolicy指定Referrer头部内容下述
signal支持通过AbortController中断进行中的fetch()请求默认未关联控制器的AbortSingle实例

cache

  • default(默认)

    命中有效缓存,不发送请求;命中无效缓存,发送条件请求更新缓存;未命中缓存发送请求
  • no-store

    不检查缓存,直接发送请求;不缓存响应
  • reload

    不检查缓存,直接发送请求;缓存响应
  • no-cache

    命中有效/无效缓存,发送条件请求并更新缓存;未命中缓存发送请求并缓存响应
  • force-cache

    命中有效/无效缓存,不发送请求;未命中缓存发送请求并缓存响应
  • only-if-cached(仅在mode:same-origin时使用)

    命中有效/无效缓存,不发送请求;未命中缓存返回504(网关超时)的响应

credentials
类似于XHR中的withCredentials

  • omit

    不发送cookie
  • same-origin(默认)

    同源下发送cookie
  • include

    同源跨源都发送cookie
    服务端Set-Cookie需要设置sameSite=None;secure

mode

  • cors(默认)

    允许遵守CORS协议的跨域请求
  • no-cors

    允许不需要发送预请求的跨源请求
  • same-origin

    禁止任何跨源请求
  • navigate

    用于支持HTML导航,只在文档间导航时使用。基本用不到

redirect

  • follow(默认)

    跟踪重定向请求,以最终非重定向URL的响应作为最终响应
  • error

    重定向请求抛出错误
  • manual

    不跟踪重定向,返回opaqueredirect类型响应,同时任然暴露期望的重定向URL,允许以手动方式跟踪重定向

referrer

  • no-referrer

    以no-referrer作为值
  • client/about:client(默认)

    以当前URL或no-referrer作为值
  • URL

    以伪造URL作为值

ReferrerPolicy

  • no-referrer

    所有请求不包含Referrer头部
  • unsafe-url

    所有请求Referrer包含完整URL
  • origin

    所有请求Referrer只包含源
  • same-origin

    跨域请求不包含Referrer
    同源请求Referrer包含完整URL
  • origin-when-cross-origin

    跨域请求Referrer只包含源
    同源请求Referrer包含完整URL
  • strict-origin

    从HTTPS上下文发送至HTTP的请求不包含Referrer头部
    其他请求Referrer只包含源
  • strict-origin-when-cross-origin

    从HTTPS上下文发送至HTTP的请求不包含Referrer头部
    其他跨域请求Referrer只包含源
    同源请求Referrer包含完整URL
  • no-referrer-when-downgrade(默认)

    从HTTPS上下文发送至HTTP的请求不包含Referrer头部
    其他请求包含完整URL

2.2 Headers对象

Headers是所有外发请求和入站响应头部的容器,Request和Response实例都有一个Headers实例
Headers对象与Map对象极其相似,都包含get、set、has、delete等实例方法

2.2.1 Headers上的API

const headers = new Headers({
    foo:'bar'
})
console.log(headers)

image.png

  • 初始化

    与Map不同的是Headers可以使用对象进行初始化
  • append

    set方法是添加或者更新一个值,append是添加或追加一个值
    因为在Header中一个字段可以有多个值
    多个值用,分隔

2.2.2 头部护卫

并非所有HTTP头部都可以被客户端修改

护卫激活限制
none初始化Headers实例时
request初始化Request对象,mode非no-cors时不允许修改禁止修改的头部
request-no-cors初始化Request对象,mode为no-cors时不允许修改简单头部
response初始化Response时不允许修改禁止修改的响应头部
immutable通过error()或redirect()初始化Response时不允许修改任何头部

2.3 Requset对象

Request对象是获取请求资源的接口

2.3.1 创建与克隆

创建Request对象
Request对象的初始化与fetch()方法类似,都接收两个参数

第一个通常为URL,第二个为init
const request = new Request('a')
console.log(request);

image.png

没有参数时初始化的值为默认值
默认的url为当前origin,headers为空

使用Request构造函数克隆Request对象

const req1 = new Request('http://localhost:8000', { body: 'foo', method: 'POST' })
const req2 = new Request(req1)
console.log(req1, req2);
console.log(req1.bodyUsed,req2.bodyUsed) //true false
克隆的副本并不会与源对象完全一致,克隆之后源对象的bodyUsed会变为true

使用clone()方法克隆Request对象

const req1 = new Request('http://localhost:8000', { body: 'foo', method: 'POST' })
const req2 = req1.clone()
console.log(req1, req2);
console.log(req1.bodyUsed, req2.bodyUsed);//false false
使用clone方法不会改变源对象bodyUsed的值
  • 如果请求对象的bodyUsed值为true(请求体已被读取)时,不能克隆

    //使用Request克隆后不能再对源对象进行克隆
    const req1 = new Request('http://localhost:8000', { body: 'foo', method: 'POST' })
    const req2 = req1.clone() //未改变bodyUsed
    const req3 = new Request(req1) //改变bodyUsed
    const req4 = req1.clone() || new Request(req1) // TypeError
    //使用text()读取后不能再克隆
    const req1 = new Request('http://localhost:8000', { body: 'foo', method: 'POST' })
    req1.text()
    cosnt req2 = req1.clone() //TypeError
    不管是用text()读取了数据还是Request克隆导致bodyUsed变化,之后都不能再对源对象进行克隆的操作

2.3.2 在fetch()中使用Request对象

可以看出Request构造函数与fetch()拥有相同的函数签名

  • 调用fetch()时,第一个参数可以不是URL而是一个Request实例对象
  • 第二个参数会复制Request的init,可以再添加进行覆盖

    //自定义头部
    const headers = new Headers({
      foo: 'bar',
    })
    headers.append('foo', 'baz')
    //初始化Request
    const req = new Request('http://localhost:8000', {
      body: 'moo',
      method: 'POST',
      credentials: 'include',
      headers
    })
    //fetch()中使用Requset对象
    fetch(req.clone())
      .then(response => response.text())
      .then(data => {
          console.log(data);
      })
    fetch(req.clone,{mode: 'no-cors',})
    如果不进行克隆就只能进行一次请求,源Request对象被读取后不能再使用
    在fetch()中使用克隆的Request对象,可以进行多个请求

2.4 Response对象

Response对象是获取资源响应的接口

创建

//Response()构造函数创建
const res1 = new Response()
console.log(res1);
//Response.redirect()创建
const res2 = Response.redirect(url,301)
//Response().error()创建
const res3 = Response.error()

image.png

初始化化Response对象时可以不添加任何参数
可接收一个body参数与init参数
  • init参数

    headers:Headers实例对象
    status:HTTP响应状态码
    statusText:HTTP响应状态的字符串

type属性

描述
errorerror静态方法创建
basic同源响应
cors跨源响应
opaqueno-cors返回的fetch()响应
opaqueredirectredirect设置为manual的请求的响应

克隆
主要使用clone()方法,与克隆Requset对象类似,克隆一个完全一致的副本,同样通过Response构造函数创建的实例在执行text()读取数据后则不能再克隆

2.5 Body混入

Request和Response都使用Fetch API的Body混入,以实现两者承担有效载荷的能力

混入为两个类型提供的内容

  • 只读的body属性-ReadableStream实现
  • 只读的bodyUsed布尔值-标志body流是否已读
  • 一组方法

Body提供的5个方法
都返回Promise

方法PromiseResult
text()将缓存区转存得到的UTF-8格式的字符串
json()将缓存区转存得到的JSON
formData将缓存区转存得到的formData实例
arrayBuffer()将缓存区转存得到的ArrayBuffer实例
blob()将缓存区转存得到的Blob实例

ReadableStream

从TCP/IP角度看,传输的数据是以分块形式抵达端点的,而且速度受到网速的限制。接收端点会为此分配内存并将收到的块写入内存。Fetch API通过ReadableStream支持在这些块到达时就实时的读取和操作这些数据

可以通过ReadableStream构造函数创建一个可读流

  • locked属性

    这个可读流是否被读取器锁定
  • getReader()方法

    创建一个读取器并将流锁定于其上。一旦流被锁定,其他读取器将不能读取它,直到它被释放。

关于Streams API的更多内容

阅读 184
37 声望
1 粉丝
0 条评论
你知道吗?

37 声望
1 粉丝
文章目录
宣传栏