刨根问底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

相关文章

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源