【web通信】Ajax与Fetch
source link: https://segmentfault.com/a/1190000040841472
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Ajax和Fecth
两者都用于无刷新更新数据:即在不刷新页面的情况下,向服务端发送请求获取数据
AJAX(Asynchronous JavaScript+XML):异步JavaScript加XML
- 关键是使用
XMLHttpRequest
(XHR)对象
Fetch:Fetch API
提供了一个原生JavaScript接口,用于访问和操纵HTTP管道的一些具体部分
- Fetch API可以实现
XHR
对象的所有任务 - 并且更容易使用,接口也更现代化
原生开发建议使用Fetch,Vue项目使用
axios
axios后续结合Vue介绍
1 XMLHttpRequest对象
- XHR的属性、方法、事件
- XHR的使用与封装
1.1 概述
- 起初XHR对象是通过
ActiveX
对象实现并包含在MSXML库中 现在的浏览器都通过
XMLHttpRequest
构造函数原生支持XHR对象(低版本IE除外)//IE var xhr1 = new ActiveXObjet('Microsoft.XMLHTTP') //主流浏览器 cosnt xhr2 = new XMLHttpRequest() console.log(xhr2)
原型上的一些属性与方法
1.2 XHR的属性、方法、事件
属性
属性描述只读readyState
请求状态1onreadystatechange
状态改变时调用0response
响应实体1responseType
定义响应类型0responseText文本数据1responseURL响应URL1responseXML可解析为XML的响应1status
状态码1statusText完整响应文本1timeout
最大请求时间0ontimeout
请求超时时调用0withCredentials布尔值,跨域请求是否可带授权信息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封装
- fetch()方法与基本使用
- Headers对象
- Request对象
- Reponse对象
- Body混入
2.1 fetch()方法与基本使用
- 使用fetch()方法
- 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允许请求存在时间超出页面生命周期默认falsemethod请求方法默认GETmode决定跨域请求的响应是否有效下述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
不发送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作为值
ReferrerPolicy
no-referrer
所有请求不包含Referrer头部
unsafe-url
所有请求Referrer包含完整URL
origin
所有请求Referrer只包含源
same-origin
跨域请求不包含Referrer
同源请求Referrer包含完整URLorigin-when-cross-origin
跨域请求Referrer只包含源
同源请求Referrer包含完整URLstrict-origin
从HTTPS上下文发送至HTTP的请求不包含Referrer头部
其他请求Referrer只包含源strict-origin-when-cross-origin
从HTTPS上下文发送至HTTP的请求不包含Referrer头部
其他跨域请求Referrer只包含源
同源请求Referrer包含完整URLno-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)
初始化
与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);
没有参数时初始化的值为默认值
默认的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()
初始化化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
ReadableStream
从TCP/IP角度看,传输的数据是以分块形式抵达端点的,而且速度受到网速的限制。接收端点会为此分配内存并将收到的块写入内存。Fetch API通过ReadableStream支持在这些块到达时就实时的读取和操作这些数据
可以通过ReadableStream构造函数创建一个可读流
locked属性
这个可读流是否被
读取器
锁定getReader()方法
创建一个读取器并将流锁定于其上。一旦流被锁定,其他读取器将不能读取它,直到它被释放。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK