XMLHttpRequest

发送请求

  1. 创建 XMLHttpRequest:
    let xhr = new XMLHttpRequest()

  2. 初始化:
    xhr.open(method, URL, [async, user, password])

method:HTTP 方法,通常 “GET” 或 “POST”
URL:请求的 URL,通常是字符串,也可以是 URL 对象
async:如果显式地设置为 false,那么请求将会以同步的方式处理(基本不用)
userpassword:HTTP 基本身份验证的登录名和密码(基本不用)

  1. 发送请求 xhr.send([body])

步骤 2~3 之间可以设置请求配置:

  • 指定响应中的数据类型:xhr.responseType,默认 text,需要服务端实际返回与之相同的类型
  • 配置在跨域时发送 cookie 和 http 授权信息:xhr.withCredentials = true
  • 设置请求体:xhr.setRequestHeader(key, value)

接下来是监听事件来获取响应

常用:

  • load:当请求完成(即使 HTTP 状态为 400 或 500 等),并且响应已完全下载
  • error:当无法发出请求,例如网络中断或者无效的 URL
  • progress:在下载响应期间周期性触发,报告已经下载了多少(可用作下载进度)
  • readystatechange:readyState 属性变化时触发(abort() 不会触发),现在基本被上面的事件替代

一旦服务器有了响应,就可以在 xhr 的属性中得到结果:

  • status:HTTP 状态码,调用 abort() 变为 0
  • statusText:HTTP 状态文本,200 对应于 OK
  • response:服务端响应正文,其值的类型取决于请求时设置的 responseType
  • readyState:了解请求当前状态

终止请求

xhr.abort()

下载进度

可以监听 xhr.onprogress 事件获取下载进度

上传进度

xhr.upload 属性上面绑定各种事件,可以通过事件的 event 参数监听上传进度:

  • onloadstart:上传开始
  • onprogress:数据传输进行中,上传期间定期触发
  • onabort:上传终止
  • onerror:非 HTTP 错误
  • onload:上传成功
  • ontimeout:上传超时
  • onloadend:上传完成(不论成功与否)

跨源请求凭据

xhr.withCredentials = true

Fetch

一种现代的网络请求方法

发送请求

基本语法:let promise = fetch(url, [options])

url:要访问的 URL
options:可选配置参数:method,header 等

浏览器会立即发送请求,并返回结果的 promise

获取响应

fetch(...).then(response => {
  if (response.ok) {
    return response.json()
  } else {
    console.log(response.status)
  }
}).then(data => {
  console.log(data)
})

分为两步:

  1. 服务器发送了响应头,fetch 返回的 promise 可以被 .then 解析为 Response 对象。此时检查 response 上的 statusok 属性判断响应结果
  2. 使用 response 上的方法获取具体格式类型的数据。如:调用 response.json() 以 JSON 格式解析响应为数据对象

上传进度

无法跟踪,该需求要使用 XHR

下载进度

response.body 是一个 ReadableStream 的对象,可以逐块(chunk)提供 body
await response.body.getReader().read() 调用的结果是一个具有两个属性的对象:

done:当读取完成时为 true,否则为 false
value:字节的类型化数组:Uint8Array

// 1.获取 reader
let response = await fetch(...)
const reader = response.body.getReader()
 
// 2.获取总长度
const contentLength = +response.headers.get('Content-Length')
 
// 3.读取数据
let receivedLength = 0
let chunks = []
while(true) {
  // 当最后一块下载完成时,done 值为 true
  // value 是块字节的 Uint8Array
  const {done, value} = await reader.read()
 
  chunks.push(value)
  receivedLength += value.length
 
  if (done) {
    break;
  }
 
  console.log(`Received ${receivedLength} bytes of ${contentLength}`)
}
 
// 4.此时得到一个 Uint8Array 字节块数组
// 4.1.可以手动拼接成 TypedArray 解析成字符串
let chunksAll = new Uint8Array(receivedLength)
let position = 0
for(let chunk of chunks) {
  chunksAll.set(chunk, position)
  position += chunk.length
}
let result = new TextDecoder("utf-8").decode(chunksAll)
 
// 4.2.也可以直接解析成二进制内容
let blob = new Blob(chunks)

注意:如果读取不到响应的 Content-Length 就不能监听下载进度

中止下载

fetch 返回的 promise 没有中止的功能。但是有一个特殊的内建对象:AbortController,它不仅可以中止 fetch,还可以中止其他异步任务

let controller = new AbortController()
有单个属性和单个方法:

  • signal
  • abort()

结合 fetch 使用:

let controller = new AbortController()
fetch(url, {
  signal: controller.signal
})
 
controller.abort() // 想要中止时调用

中止后:fetch 返回的 promise 会 reject

跨源请求凭据

由 js 发起的跨域请求不会带上任何凭据(cookie 和 HTTP 认证)
在配置项加上 credentials: "include",可以带上凭据

fetch('http://xxx.com', {
  credentials: "include"
})

POST 请求体

字符串

如常用的 JSON 字符串,请求 header 设置 'Content-Type': 'application/json; charset=utf-8'

FormData 对象

用来上传文件:'Content-Type': "multipart/form-data"
FormData 是可迭代的

Blob / BufferSource

发送二进制数据,Content-Type 的值是 Blob 内建的类型

URLSearchParams

键值对形式提交数据:"a=hello&b=world"content-type: "x-www-form-urlencoded"

WebSocket

在浏览器和服务器之间建立持久连接

Server Sent Events

服务器单向推送消息到浏览器,使用 EventSource 实现

对比:

WebSocketEventSource
双向:客户端和服务端可以互相交互信息单向:仅服务端向客户端发送信息
文本和二进制文本
WebSocket 协议HTTP 协议