跳到主要内容

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 格式給后端?

  1. new FormData() or new URLSearchParams() 然後自己拼裝。
  2. 將表單 form 元素用 submit()方式送出

使用new FormData()转换form格式

ts
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返回数据给客户端

js
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() 方法来取消请求。
js
var xhr = new XMLHttpRequest();

xhr.open("GET", url);
xhr.send();

// 在某些时候,您可以调用 xhr.abort() 来取消请求
xhr.abort();

fetch取消请求:​

  • 常用的方法是使用 AbortController 和 AbortSignal 对象来实现取消请求
    • AbortController 是一个控制器对象,你可以通过 new 构造函数的形式返回一个 AbortSignal 对象
      • 可以控制请求的中止状态

AbortSignal 接口表示一个信号对象(signal object),它允许你通过 AbortController 对象与 Fetch 进行通信并在需要时将其中止请求

AbortSignal 简单来说就是 用来传递中止信号

js
const controller = new AbortController();
const signal = controller.signal;

fetch(url, { signal }).then(response => {
// 处理响应
}).catch(error => {
// 处理错误
});

// 在某些时候,您可以调用 controller.abort() 来取消请求
controller.abort();

axios取消请求:​

  • Axios使用CancelToken
js
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事件
js
const xhr = new XMLHttpRequest()
// 超时设置 2s 设置(超过2s请求取消)
xhr.timeout = 2000
// 超时回调
xhr.ontimeout = function () {
alert('网络异常,请稍后重试!!!')
}

fetch超时处理​

  • fetch是不支持超时处理的
  • 可通过AbortController 控制器来实现 配合 setTimeout
js
// 利用高阶函数封装 保证通用性、保持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请求,就能重新请求

js
// 核心代码
return axios(config) // 能够进行请求失败重试

拦截器​

js
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事件拿到字节数、总字节数来实现
javascript
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压缩,获取的文件大小会比实际文件大小要小
javascript
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