ajax/fecth
ajax
1、允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax., 交互式网页应用的网页开发技术
2、不用重新刷新整个页面,只刷新局部页面,就可以与服务器交换数据
3、异步javascript发送
4、客户端通过ajax向服务器发送数据
5、ajax向服务器获取数据后,ajax返回数据给客户端
get和post的请求本质的区别
- get比post更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- get参数通过URL传递,post放在Request body中。
- get发送的数据长度有限
- get请求会被浏览器cache,而post不会缓存
相同点:
- get和post是TCP连接
- get产生一个TCP数据包;post产生两个TCP数据包。
什么是Content-Type
- ContentType指的是请求体的编码类型,常见的类型共有3种:
使客户端浏览器,区分不同种类的数据,并根据不同的MIME(媒体类型 【用来表示文档、文件或字节流的性质和格式】)调用浏览器内不同的程序嵌入模块来处理相应的数据。
(浏览器通常使用MIME类型(来确定如何处理URL)
1.application/x-www-form-urlencoded
- 浏览器原生表单
<form>默认的提交数据的方式(就是没有设置enctype属性),POST提交数据的默认方式。- 一般只用于上传文件
2.multipart/form-data
- 这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让
<form>表单的enctype等于 multipart/form-data。直接来看一个请求示例:
3.application/json
-
application/json 这个 Content-Type 常作为作为响应头,现在页经常使用它作为请求头,用来告诉服务端消息主题是序列化后的JSON字符串。
由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦
formData和json
需要传文件采使用 form-data,其它情况一律用 JSON
- 由于form-data传递二进制比较方便,不然可以完全舍弃。json 上传文件不是很合适
- formData:Content-Type为application/x-www-form-urlencoded
- json:Content-Type为application/json
- json表示已经序列化好了
到底怎麼传递form 格式給后端?
new FormData()ornew URLSearchParams()然後自己拼裝。- 將表單 form 元素用 submit()方式送出
使用new FormData()转换form格式
function toFormData(raw: Record<string, string>): FormData {
const data = new FormData()
forEach(raw, (v, k) => data.append(k, v))
return data
}
拦截器
- 请求拦截、响应拦截
请求拦截
- 在请求的时候,在请求头加上token,后台根据token判断登录状态
响应拦截
- 对响应的数据进行处理
手写原生ajax
1、允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax., 交互式网页应用的网页开发技术
2、不用重新刷新整个页面,只刷新局部页面,就可以与服务器交换数据
3、异步javascript发送
4、客户端通过ajax向服务器发送数据
5、ajax向服务器获取数据后,ajax返回数据给客户端
const request = (url) => {
return new Promise((resolve) => {
// 1:创建Ajax对象
const xhr = new XMLHttpRequest();
// 2:配置 Ajax请求地址
xhr.open("get", url);
// 3:发送请求
xhr.send(null); // 严谨写法
// 4:监听请求,接受响应
xhr.onreadystatechange = function () {
// readySates == 4 表示 请求已完成,且响应已就绪
// xhr.status == 200 表示请求成功
// 304 协商缓存
if ((xhr.readyState == 4 && xhr.status == 200) || xhr.status == 304){
resolve(JSON.parse(xhr.responseText));
}
};
});
};
请求超时处理
cancelToken取消请求
应用场景:当用户重新刷新数据请求的时候,如果你之前发起的请求列表还没有响应,这时候如果你重新发起请求,会出现二次请求的情况,可以通过cancelToken可以取消上一次请求
作用:支持取消请求 (cancelToken)
- 如果在短时间内发生多个请求,之前发起的请求还没有响应,这时重新请求,会出现二次请求(多次请求情况),可能会导致导致获取数据不匹配 (获取了上一次请求的数据),可以通过cancelToken可以取消上一次请求
- 搜索栏的情况
中断请求
-
axios.cancelToken
-
fetch中断请求,可以使用原生的AbortController接口
-
umi-request中断请求:umi-request 基于 fetch 封装, 兼具 fetch 与 axios 的特点, 中止请求与 fetch 和 axios 一致,既可以使用cancelToken也可以使用AbortController
ajax轮询
- ajax轮询也可以替代websocket实时消息接收
- 轮询:客户端按规定时间定时向服务端发送ajax请求,服务器接到请求后马上返回响应信息
- Ajax轮询需要服务器有很快的处理速度与快速响应。
axios
- 是对ajax的封装,基于 Promise
- 支持浏览器和 Node.js
- 支持请求参数转换和序列化:axios 自带了请求参数转换和序列化的功能,能够很方便地处理复杂的请求参数
- 支持自定义拦截器:axios 支持自定义拦截器,可以用来拦截请求和响应,实现更复杂的功能。
fecth
fetch:是基于promise的封装,是原生的API
- 原生fetch 本身并不提供请求超时、缓存、取消请求等能力,而在业务开发中却常常需要
- 没有办法做请求的进度处理,而ajax可以通过onprogress事件可以处理进度
fetch如何做polyfill
由于fetch是基于promise, 需要promise的polyfill
- promise的polyfill,例如es6-promise、babel-polyfill提供的promise实现。
- 首先判断浏览器是否原生支持fetch,否则结合Promise使用XMLHttpRequest的方式来实现
fetch和axios的区别:
- 兼容性,fetch原生的js方法,在IE浏览器不支持,需要 polyfill
- axios是对ajax的封装,而fetch是原生js,并不是对ajax的封装
umi-request
- umi-reques基于 fetch 封装的开源 http 请求库
- size:9k大小
特点:
- URL 参数自动序列化
- POST 数据提交方式简化
- 请求超时处理
- 请求缓存支持
- 错误检查、错误处理
- 拦截器
相比axios的优点是什么?
- umi-request支持缓存,axios没有缓存
- umi-request支持错误检查,axios没有错误检查,只有错误处理(错误拦截)
- umi-request支持前缀和后缀,axios不支持
- umi-request支持中间件的方式,axios不支持
相比原生fecth的优点是什么?
- 原生fetch 本身并不提供请求超时、缓存、取消等能力,而在业务开发中却常常需要
- 因此umi-request就封装了这些能力(支持超时,缓存,取消),减少重复开发,提高效率
- umi-request提供中间件机制方便拓展,可以定制化处理需求
- 类似koa的中间件
- umi-request 不仅支持 browser http 请求,还可以支持node环境
- 在node里,进行请求转发
如何取消请求
ajax取消请求
- 在使用 XMLHttpRequest 发送请求时,您可以使用
abort()方法来取消请求。
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
// 在某些时候,您可以调用 xhr.abort() 来取消请求
xhr.abort();
fetch取消请求:
- 常用的方法是使用 AbortController 和 AbortSignal 对象来实现取消请求
- AbortController 是一个控制器对象,你可以通过 new 构造函数的形式返回一个 AbortSignal 对象
- 可以控制请求的中止状态
- AbortController 是一个控制器对象,你可以通过 new 构造函数的形式返回一个 AbortSignal 对象
AbortSignal接口表示一个信号对象(signal object),它允许你通过AbortController对象与 Fetch 进行通信并在需要时将其中止请求AbortSignal 简单来说就是 用来传递中止信号
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal }).then(response => {
// 处理响应
}).catch(error => {
// 处理错误
});
// 在某些时候,您可以调用 controller.abort() 来取消请求
controller.abort();
axios取消请求:
- Axios使用CancelToken
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get(url, { cancelToken: source.token }).then(response => {
// 处理响应
}).catch(error => {
// 处理错误
});
// 在某些时候,您可以调用 source.cancel() 来取消请求
source.cancel();
如何超时处理
当前端向后端发起数据请求时,后端一直没有返回响应结果,不可能让用户一直等待下去,所以当请求超过一定的时间,就要提示用户或者取消这个请求
ajax超时处理
- XHR是支持超时处理的
- 通过timeout属性以及ontimeout事件
const xhr = new XMLHttpRequest()
// 超时设置 2s 设置(超过2s请求取消)
xhr.timeout = 2000
// 超时回调
xhr.ontimeout = function () {
alert('网络异常,请稍后重试!!!')
}
fetch超时处理
- fetch是不支持超时处理的
- 可通过AbortController 控制器来实现 配合 setTimeout
// 利用高阶函数封装 保证通用性、保持fetch功能不变
function createFetch(timeout) {
return (resource, options) => {
const controlller = new AbortController();
options = options || {};
options.signal = controlller.signal;
setTimeout(() => {
controlller.abort();
}, timeout);
return fetch(resource, options);
};
}
createFetch(2000)('url');
如何做请求超时重试?
什么场景需要超时重试? 一般情况下,不需要超时重试处理,请求重试一般是当网络异常,网络错误、断网的情况下,可以重试处理,在客户端比如小程序会用的比较多 超时时间一般是3-5秒的时间 请求重试需要考虑三个因素:
- 重试条件。比如网络错误,才会进行重试条件,
- 重试次数
- 重试延迟时间
axios超时重试处理
主要是当发生错误时,返回axios请求,就能重新请求
// 核心代码
return axios(config) // 能够进行请求失败重试
拦截器
axios.interceptors.response.use(null, (err) => {
let config = err.config;
if (!config || !config.retryTimes) return Promise.reject(err);
const { __retryCount = 0, retryDelay = 300, retryTimes } = config;
// 在请求对象上设置重试次数
config.__retryCount = __retryCount;
// 判断是否超过了重试次数
if (__retryCount >= retryTimes) {
return Promise.reject(err);
}
// 增加重试次数
config.__retryCount++;
// 延时处理
const delay = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, retryDelay);
});
// 重新发起请求
return delay.then(function () {
return axios(config);
});
})。
适配器
1、适配器通过封装 XMLHttpRequest API 来发送 HTTP 请求
如何读取进度
ajax读取进度
- XHR是支持progress事件的, 通过progress事件拿到字节数、总字节数来实现
var xhr = new XMLHttpRequest()
xhr.open('POST', '/uploads')
xhr.onload = function() {}
xhr.onerror = function() {}
function updateProgress (event) {
if (event.lengthComputable) {
var percent = Math.round((event.loaded / event.total) * 100)
console.log(percent)
}
xhr.upload.onprogress =updateProgress; //上传的progress事件
xhr.onprogress = updateProgress; //下载的progress事件
}
xhr.send();
fetch读取进度
注意:fetch 方法无法跟踪 **上传 **进度,fetch是不支持progress事件,但是内部实现了Request和Response类, 可以跟踪 下载 进度
-
可通过Response实例访问 response.body
-
response.body是可读的字节流对象,可通过getRender()来读取响应
- 该字节流是可以循环读取的,直至body内容传输完成;
-
response.headers的
content-length获取总字节大小- 注意:如果使用gzip压缩,获取的文件大小会比实际文件大小要小
const RequestPercent = async ({
url = '',
option = {
headers: {
'Content-Type': 'application/pdf',
responseType: 'arraybuffer',
},
},
onprocess,
}) => {
const response = await fetch(url, option);
const reader = response?.body.getReader();
// 文件总长度
const contentLength = +response.headers.get('content-length');
let receivedLength = 0;
const chunks = [];
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
onprocess(receivedLength, contentLength);
}
return chunks;
};
如何携带Cookies
axios和fetch默认都不会携带cookies,需要配置,设置跨域请求时发送Cookie
fetch如何携带Cookies
fetch默认不会带cookie,需要添加配置项。
- 设置credentials属性为include
- omit: 默认值,忽略cookie的发送
- same-origin: 表示cookie只能同域发送,不能跨域发送
- include: cookie既可以同域发送,也可以跨域发送
axios如何携带Cookies
- 设置withCredentials属性设为 true