刨根问底ajax原理与封装

2023-11-22 18:41

本文主要是介绍刨根问底ajax原理与封装,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说起ajax,大家都不陌生。但是由于目前很多框架或者库等都对网络请求做了封装,导致了很多初学者只知其然而不知其所以然。所以今天我们就详细了解一下ajax的实现原理和封装ajax的关键步骤。

ajax的核心是XMLHttpRequest对象。首先我们先创建一个XMLHTTPRequest对象 var xhr = new XMLHttpRequest();

注意:本文所提及的内容不兼容古老的IE,有想了解的同学自行查阅ActiveXObject相关内容。

XMLHttpRequest

在使用XMLHttpRequest对象的第一步,我们首先要调用open方法来初始化请求参数,xhr.open('get','/test',true),虽然名字叫open,但是此时请求还并没有发送。

open(method, url[, async, username, password])

  • method:请求类型,例如GET,POST等
  • url:请求地址(这里有同源限制,就是我们经常会看到的跨域问题啦)
  • async:是否发送异步请求。可选参数,默认为true。
  • username&password:可选参数,授权验证使用的,但是我们一般不这么用,使用后请求变成这个样子了,http(s)://username:password@url。
如果调用了open方法后再次对它进行调用,则相当于调用了abort方法,abort方法我们在后面介绍。

如果我们想为为请求绑定一些操作,这个时候就可以开始啦。常用的操作有如下几个:

setRequestHeader(key, value)

顾名思义,这个方法用于设置请求头内容。

  • key:要设置的请求头名称
  • value:对应请求头的值

overrideMimeType(type)

重写服务器返回的MIME类型。通过这个方法可以告诉服务器你想要的数据类型。

注意:以上这些操作必须定义在send方法之前。否则,就拿setRequestHeader来说,你都把请求发出去了再设置还有什么用?

这个时候,我们就可以通过调用send 方法来发送请求了,xhr.send(null)

send(data)

发送请求,如果是同步请求的话,会阻塞代码的执行,直至收到服务器响应才会继续。

  • data:发送给服务器的数据。为了兼容不同的浏览器,即使是不需要传数据,也需要传入参数null。

readyStateChanhe()

每次readyState的值改变的时候都会触发这个函数。

getResponseHeader(name)

获取指定响应头部的值,参数是响应头部的名称,并且不区分大小写。

getAllResponseHeaders()

获取服务器发送的所有HTTP响应的头部。

在这里我们穿插几个概念,readyState,这个属性表明了请求的状态,伴随HTTP请求的整个生命周期,它的值表明此时请求所处的阶段,具体如下:

readyState

数值描述
0初始化,open()尚未调用
1open()已经调用,但是send未调用
2已获取到返回头信息
3正在下载返回体信息
4请求完成

还有几个较为常用的属性

名称含义
responseText响应的文本
status响应的状态码
statusText响应的状态信息
responseXML响应内容是“text/xml”或者是“application/xml”格式的时候,这个属性的值就是响应数据的XMLDOM文档。

我们用下面这段代码做个测试

var xhr = new XMLHttpRequest();
console.log(xhr.readyState)
xhr.onreadystatechange = function(){console.log('------')console.log('readyState:' + xhr.readyState)console.log('ResponseHeaders:' + xhr.getAllResponseHeaders())console.log('ResponseText:' + xhr.responseText.length)console.log('------')
}
xhr.open('get','/')
xhr.send(null)

下图我们可以直观的看到在创建了XMLHttpRequest对象的时候,readyState的值为0。

image
然后我们定义了onreadystatechange函数,让其打印一些属性,并调用open方法,此时readyState变为1。

image
最后我们调用send方法,可以看到经历了如下过程:

  1. send方法调用之后,readyState变为2,此时responseHeader已经获取到了,responseText为空;
  2. 响应数据开始下载,readyState变为3
  3. 响应数据下载结束,readyState变为4.我们可以发现此时responseText的长度比之前长。

image

abort()

取消响应,调用这个方法会终止已发送的请求。我们尝试在之前的代码最后加一句。

xhr.abort();
console.log(xhr.readyState);

image
image

也就是说,send执行以后,并没有去尝试请求数据,而是直接取消掉了,并且我们发现abort会将readyState的值置为0。

除此之外,XMLHttpRequest还有一个很重要的属性withCredentials,cookie在同域请求的时候,会被自动携带在请求头中,但是跨域请求则不会,除非把withCredentials的值设为true(默认为false)。同时需要在服务端的响应头部中设置Access-Control-Allow-Credentials:true。不仅如此Access-Control-Allow-Origin的值也必须为当前页面的域名。


封装

到此为止,我们终于讲完了XMLHttpRequest的一些常用概念。接下来,我们尝试自己封装一个支持get和post的简易jax请求。

function ajax(url, option){option = option || {};var method = (option.method || 'GET').toUpperCase(),async = option.async === undefined ? true : option.async,params = handleParams(option.data);var xhr = new XMLHttpRequest();if(async){xhr.onreadystatechange = function () {if (xhr.readyState == 4){callback(option,xhr);}};}if (method === 'GET'){xhr.open("GET",url + '?' + params, async);xhr.send(null)}else if (method === 'POST'){xhr.open('POST', url, async);xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xhr.send(params);}if(!async){callback(option,xhr);}function callback(opt,obj){var status = obj.status;if (status >= 200 && status < 300 ){opt.success && opt.success(obj.responseText,obj.responseXML);}else{opt.fail && opt.fail(status);}}function handleParams(data) {  var arr = [];for (var i in data) {arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));}return arr.join('&');}
}
//  测试调用
ajax('/xxx',{method:'POST',data:{key: 'test'},success:function(){console.log('success')},fail:function(){console.log('fail')}
});

小结

其实ajax实现原理并不复杂,复杂的是容错、兼容性等的处理,使用的时候尽量使用库或者框架提供的封装,避免不必要的漏洞。


补充

感谢@虾哔哔的提问,这里做个简单的补充说明。

async是一个可选的布尔值参数,默认为true,意味着是否执行异步操作,如果值为false,则send()方法不会返回任何东西,直到接受到了服务器的返回数据。如果为值为true,一个对开发者透明的通知会发送到相关的事件监听者。这个值必须是true,如果multipart 属性是true,否则将会出现一个意外。

根据我的分析,当async为false的时候,readyState会在send方法之后直接由1变成4。也就是说异步模式,send方法会立刻返回。同步模式下,只有响应完全接受后,send才会返回。

另外,由于同步模式会阻塞,较新版本的Chrome在主线程上的同步请求已被弃用。

这篇关于刨根问底ajax原理与封装的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/412141

相关文章

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

MySQL中的MVCC底层原理解读

《MySQL中的MVCC底层原理解读》本文详细介绍了MySQL中的多版本并发控制(MVCC)机制,包括版本链、ReadView以及在不同事务隔离级别下MVCC的工作原理,通过一个具体的示例演示了在可重... 目录简介ReadView版本链演示过程总结简介MVCC(Multi-Version Concurr

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu