对于Ajax,肯定很多小伙伴都听过甚至用过了,那么没听过的也不用着急,本文会对Ajax进行讲解,其次,一定还有一些人只用过JQuery封装好了的Ajax却对原生的Ajax并不了解,那么也不用着急,本文从最基本的Ajax开始讲起,然后最后会尽可能得模仿JQuery对其进行封装,让我刚才提到的两类人能对Ajax有进一步的了解。

  • 公众号:前端印象
  • 不定时有送书活动,记得关注~
  • 关注后回复对应文字领取:【面试题】、【前端必看电子书】、【数据结构与算法完整代码】、【前端技术交流群】

异步编程——Ajax

  • 一、什么是Ajax
  • 二、Ajax的优缺点
    • (1)优点
    • (2)缺点
  • 三、Ajax的使用
    • (1)状态码
    • (2)xhr的基本使用
    • (3)发送get请求
    • (4)发送post请求
  • 四、封装Ajax
    • (1)JQuery中的Ajax
    • (2)封装准备工作
    • (3)封装$.get方法
    • (4)封装$.post方法
    • (5)封装$.ajax方法
  • 五、Ajax的约束
  • 六、结束语

一、什么是Ajax

Ajax(Asynchronous JavaScript And XML)是2005年新出现的技术,它的出现是为了解决这样一个场景:整个页面中,只有一小部分的数据需要进行更新,按照传统的前后端交互,我们需要向服务器请求该网页的所有数据,然后再在客户端重新渲染,这无疑是非常低效的操作。因此,Ajax就可以做到只向服务器请求我们想要的那一小部分数据,而不用请求全部数据,进而在刷新整个页面的前提下更新那部分的数据。

举个例子,我们去饭店吃饭,然后点了一桌子菜,后来发现其中有一道菜太咸了,因此我们只需要让服务员端回去给厨师重新做这一道菜再拿回来就行了。

在这个例子中的人、物对比Ajax的关系如下表:

吃饭事件 数据更新
我们 客户端
菜品 页面所有的数据
服务员 ajax对象
厨师 服务器

当我们发现有一道菜太咸了,不需要让厨师把所有的菜重新做一遍,只要让服务员拿这一道菜回去给厨师重做这一操作就相当于让ajax对象向后端请求那一小部分数据再拿回来更新页面而无需刷新整个页面。

二、Ajax的优缺点

了解了Ajax的作用和定义,我们再来看看它的优缺点

(1)优点

  1. 浏览器默认支持(一般浏览器都是支持JavaScript的)
  2. 提高用户体验(不需要刷新整个页面,而只需要局部刷新)
  3. 提高页面的性能(只需要请求部分数据,所以数据量就明显下降了)

(2)缺点

  1. 破坏了浏览器的前进和后退功能(Ajax不会改变网页URL,因此不会在浏览器记录前后页面)
  2. 对搜索引擎的支持较弱(搜索引擎无法监测到JS引起的数据变化)

三、Ajax的使用

Ajax的基本流程:创建XHR对象 => 发送数据 => 接收数据

(1)状态码

既然Ajax涉及到前后端的数据交互,那么我们就先来简单的看一下几种类型的状态码,如下表:

状态码 含义
100 ~ 199 连接继续
200 ~ 299 各种成功的请求
300 ~ 399 重定向
400 ~ 499 客户端错误
500 ~ 599 服务端错误

(2)xhr的基本使用

在使用xhr之前,我们要创建一个xhr的实例对象

let xhr = new XMLHttpRequest()

然后再调用xhr对象上的 open() 方法,表示创建一个请求。

open() 方法接收三个参数:

  • 第一个参数: 请求的类型(例如get 、post)
  • 第二个参数: 请求的URL
  • 第三个参数: 是否异步发送请求(默认为true)
// 创建了一个Ajax请求
xhr.open('get', 'example.php', 'true')

光调用了 open() 方法还不够,它只是创建了一个请求,但还没有发送请求,因此我们还要调用xhr对象上的另一个方法,即 send() 方法,表示将请求发送给目标URL

send() 方法接收一个参数:

  • 第一个参数: 作为请求主体发送的数据(例如post请求携带的数据)
// 我们上面创建的是get请求,因此send()方法无需传参
xhr.send()

请求发送出去后,客户端需要接收服务器响应回来的数据,xhr对象中有一些属性,它们存储着服务端返回来的一些数据信息,如下表所示

属性名 含义
responseText 服务端返回的文本信息
responseXML 服务端返回的XML DOM文档
status HTTP状态码
statusText HTTP状态码说明
readyState xhr对象的请求响应阶段

既然我们要获取服务端返回的数据,我们就要知道服务端是何时返回数据的,这就可以通过上面表格中的 readyState 属性来判断了

readyState 属性一共有5个值,分别表示不同的请求响应阶段:

  • 0: 还未创建请求,即未调用 open() 方法
  • 1: 已调用 open() 方法,但未发送 send() 方法
  • 2: 已调用 send() 方法,但未接收到响应
  • 3: 已接收到部分响应
  • 4: 已接收到全部的响应

同时,xhr对象可以绑定一个 readystatechange 事件,每当 readyState 属性发生改变,都会触发该事件,因此,该事件在一次请求中会被多次触发

xhr.onreadystatechange = function() {console.log('readyState属性发生改变了')
}

所以,我们可以在 readystatechange 事件中判断一下 readyState 属性是否为 4,即是否已经接收所有的响应,然后还可以再继续判断一下 status 属性,看看状态码是否为 200,当上述都成立了,我们再去 responseText 属性 或 responseXML 属性中获取响应数据

xhr.onreadystatechange = function() {// 判断是否已接收所有响应if(xhr.readyState === 4) {// 判断状态码是否为200if(xhr.status === 200) {console.log(xhr.responseText)}}
}

(3)发送get请求

上面也讲解了Ajax请求的简单应用,同时也是拿 get 请求来举得例子,因此这里我就不多做说明,唯一要讲的就是,get请求所携带的数据是明文的,大小只有4k左右,而且它是写在URL的 ? 后面的,例如这样 example.php?query=4&em=0,所以若是我们要在发送get请求时携带数据,只需要在调用 open() 方法时,将数据写在第二个参数的URL的 ? 后面即可

直接来写一次完整的 get 请求,代码如下:

let xhr = new XMLHttpRequest()
xhr.open('get', 'example.php?query=4&em=0')
xhr.send()
xhr.onreadystatechange = function() {if(xhr.readyState === 4) {if(xhr.status === 200){console.log(xhr.responseText);}}
}

(4)发送post请求

发送post请求的过程几乎和get请求一样,唯一不一样的是数据的传递。大家都知道post请求的数据是放在请求体中的,因此我们需要调用xhr对象上的 setRequestHeader() 方法来模仿表单提交时的内容类型

该方法传入的参数比较固定,代码如下

xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

然后我们上面也说过,send() 方法接收的一个参数是请求主体发送的数据,所以我们的post请求要发送的数据就要作为该方法的参数,代码如下:

xhr.send('query=4&em=0')

那我们来看一次完整的post请求是怎么样的吧,代码如下:

let xhr = new XMLHttpRequest()
xhr.open('post', 'example.php')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send('query=4&em=0')
xhr.onreadystatechange = function() {if(xhr.readyState === 4) {if(xhr.status === 200){console.log(xhr.responseText);}}
}

四、封装Ajax

文章开头提到,JQuery早已对Ajax请求进行了成熟的封装,所以我们可以借鉴它,甚至尽可能地去模仿它进行封装,在这之前,我们得先了解JQuery中Ajax的使用

(1)JQuery中的Ajax

这里我找来了几段使用JQuery发送Ajax请求的代码,如下所示:

  • 发送get请求
$.get('example.php', {query: 4, em: 0}, function(data, status, xhr) {console.log(`返回的数据为${data}返回的状态为${status}返回xhr对象为${xhr}`)
}, 'json')

这段代码发送了一个 get 请求,携带的参数有 query 值为 4em 值为 0,规定返回的数据类型为 json,同时设定了一个回调函数用于接收请求返回的数据、状态和xhr对象

  • 发送post请求
$.post('example.php', {query: 4, em: 0}, function(data, status, xhr) {console.log(`返回的数据为${data}返回的状态为${status}返回xhr对象为${xhr}`)
}, 'json')

这段代码发送了一个 post 请求,携带的参数有 query 值为 4em 值为 0,规定返回的数据类型为 json,同时设定了一个回调函数用于接收请求返回的数据、状态和xhr对象

  • 综合方法
// 该方法既可以发送get请求又可以发送post请求
$.ajax({url: 'example.php', // 请求的URLtype: 'get', //请求类型,若为post,则表示发送post请求data: {query: 4, em: 0},     // 请求携带数据dataType: 'json',  // 接收的数据类型isAsync: true     // 是否异步请求
})
.then(data => {console.log(`请求成功,数据为${data}`)
})
.catch(err => {console.log(`请求失败,状态为${err}`)
})

其调用的是一个综合的方法,传入的参数是一个对象,对象中传入多个参数。这段代码是发送了一个 get 请求,地址为 example.php,携带的参数有 query 值为 4em 值为 0,所接收返回数据的类型为 json,请求为异步请求

特别的是,该方法的回调函数是通过 promise 实现的,即该方法返回一个 promise 对象,在 then 函数中处理请求成功的情况,在 catch 函数中处理请求失败的情况

若没有了解过 promise 的小伙伴建议先花几分钟了解一下,因为这是异步编程最常用的一个语法,下面放上文章链接——深入了解Promise对象,写出优雅的回调代码,告别回调地狱

接下来我们就针对上述给出的例子,逐个封装

(2)封装准备工作

因为 XMLHttpRequest 对象有一定的兼容性,因此我们在封装ajax方法之前可以先封装一个方法用来动态创建一个兼容性稍微好点的XHR对象(其中主要是兼容IE5和IE6)

我们都知道JQuery都是将方法封装在一个名为 $ 的对象中的,我们也这么做

let $ = {createXHR: function() {// 若浏览器支持,则创建XMLHttpRequest对象if(window.XMLHttpRequest) {return new XMLHttpRequest()} // 若不支持,则创建ActiveXobject对象else {return new ActiveXObject()} }
}

(3)封装$.get方法

首先查阅JQuery的 get 方法可知,其接收四个参数:URLdatacallbackdataType,分别表示请求的url地址、携带的参数、成功回调函数、返回数据的类型

let $ = {// 动态生成XHR对象的方法createXHR: function() {if(window.XMLHttpRequest) {return new XMLHttpRequest()} else {return new ActiveXObject()} },get: function(url, data, callback, dataType) {// 避免dataType大小写的问题let dataType = dataType.toLowerCase()// 如果有传入data,则在url后面跟上参数if(data) {url += '?'Object.keys(data).forEach(key => url += `${key}=${data[key]}&`)url = url.slice(0, -1)}// 调用我们封装的方法生成XHR对象let xhr = this.createXHR()// 创建get请求xhr.open('get', url)// 发送请求xhr.send()xhr.onreadystatechange = function() {if(xhr.readyState === 4) {if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {// 若dataType为json,则将返回的数据通过JSON.parse格式化let res = dataType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseText// 调用回调函数,并把参数传进去callback(res, xhr.status, xhr)}}}},
}

(4)封装$.post方法

JQuery的 post 方法传入的参数跟 get 方法一样,只不过其内部的实现有略微的区别,就是携带参数的发送不一样,所以直接来看代码吧

let $ = {// 动态生成XHR对象的方法createXHR: function() {if(window.XMLHttpRequest) {return new XMLHttpRequest()} else {return new ActiveXObject()} },post: function(url, data, callback, dataType) {// 避免dataType大小写的问题let dataType = dataType.toLowerCase()// 调用我们封装的方法动态生成XHR对象let xhr = this.createXHR()let str = ''// 若传入参数,则将参数序列化if(data) {Object.keys(data).forEach(key => str += `${key}=${data[key]}&`)str = str.slice(0, -1)}// 设置头部信息xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')// 发送请求,并携带参数xhr.send(str)xhr.onreadystatechange = function() {if(xhr.readyState === 4) {if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {// 若dataType为json,则将返回的数据通过JSON.parse格式化let res = dataType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseText// 调用回调函数,把对应参数传进去callback(res, xhr.status, xhr)}}}}
}

(5)封装$.ajax方法

在JQuery中还有一个 ajax 方法,其既可以发送 get 请求,也可以发送 post 请求,该方法可传入多种参数,且支持 promise 处理回调函数

let $ = {createXHR: function() {if(window.XMLHttpRequest) {return new XMLHttpRequest()} else {return new ActiveXObject()} },ajax: function(params) {// 初始化参数let type = params.type ? params.type.toLowerCase() : 'get'let isAsync = params.isAsync ? params.isAsync : 'true'let url = params.urllet data = params.data ? params.data : {}let dataType = params.dataType.toLowerCase()// 用我们封装的方法动态生成XHR对象let xhr = this.createXHR()let str = ''// 拼接字符串Object.keys(data).forEach(key => str += `${key}=${data[key]}&`)str = str.slice(0, -1)// 如果是get请求就把携带参数拼接到url后面if(type === 'get') url += `?${str}`;// 返回promise对象,便于外部then和catch函数调用return new Promise((resolve, reject) => {// 创建请求xhr.open(type, url, isAsync)if(type === 'post') {xhr.setRequestHeader('Content-Type', 'application/x-www-form-rulencoded')xhr.send(str)} else {xhr.send()}xhr.onreadystatechange = function() {if(xhr.readyState === 4) {if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {let res = dataType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseTextresolve(res) // 请求成功,返回数据} else {reject(xhr.status) // 请求失败,返回状态码}}}})   }
}

五、Ajax的约束

默认情况下,Ajax一般只能向同源的域发送请求,这是受到了浏览器的同源策略的限制,关于同源策略,你们可以去看一下我以前写过的一篇博客,里面写了同源策略的定义以及解决方案——前端人员都懂的浏览器的同源策略,以及如何进行不同源间的相互访问

了解过同源策略以后,我们来看看如何让Ajax不受同源策略的限制而成功发送请求。CORS(跨域资源共享)要求我们在发送请求时自定义一个HTTP头部与服务器进行沟通,我们只需要设置一个名为 Origin 的头部,值为当前页面的源信息(协议、域名、端口),例如 Origin : http://example.com ;然后服务器需要设置一个名为 Access-Control-Allow-Origin 的响应头部,其值为允许跨域访问的源信息,若服务器设置的 Access-Control-Allow-Origin 与我们设置的 Origin 相同,则表示服务器允许我们跨域请求其资源,或者服务器可以将 Access-Control-Allow-Origin 值设为 *,此时表示允许任何域向其发送请求并且不受同源策略的限制。

现在的大部分浏览器几乎都支持了在发送Ajax请求后,自动向请求头部添加当前的源信息

六、结束语

建议你们好好了解JS的Ajax的使用,这样在面试中问起来你还能说出个一二三,并且有时候面试官还会直接让你亲手写一个简单的Ajax请求呢,而不会让你使用JQuery的。看了本文,想必面试官如果让你当场封装一个类似JQuery的Ajax请求,你也不会手足无措呢

欢迎关注公众号:前端印象 , 不定时更新前端面试题,与我一起学习前端,早日斩获大厂Offer

异步编程Ajax的详解,并对其进行封装整理相关推荐

  1. Java 8 的异步编程利器 CompletableFuture 详解

    文章目录 一个例子回顾 Future 一个例子走进CompletableFuture CompletableFuture使用场景 创建异步任务 supplyAsync方法 runAsync方法 任务异 ...

  2. java ajax同步请求,成都汇智动力-java ajax实现异步同步请求全面详解

    原标题:成都汇智动力-java ajax实现异步同步请求全面详解 对象 var request=new () //兼容IE5 IE6 if (window.) {// code for IE7+, F ...

  3. Java编程配置思路详解

    Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...

  4. 【前端用法】$.ajax()方法详解,以及$.ajax()标准写法

    使用JQuery中的异步请求$.ajax()方法,经常记不全参数,所以在这里记录一下,方便后续使用. 首先看一个标准的写法: $(document).ready(function () {$.ajax ...

  5. Java并发编程最佳实例详解系列

    Java并发编程最佳实例详解系列: Java并发编程(一)线程定义.状态和属性 Java并发编程(一)线程定义.状态和属性 线程是指程序在执行过程中,能够执行程序代码的一个执行单元.在java语言中, ...

  6. $.ajax实例详解,Ajax与$.ajax实例详解

    这次给大家带来Ajax与$.ajax实例详解,Ajax与$.ajax使用的注意事项有哪些,下面就是实战案例,一起来看一下. 实例一(Ajax请求基本创建格式): Ajax练习(GET,不考虑浏览器兼容 ...

  7. python协程详解_对Python协程之异步同步的区别详解

    一下代码通过协程.多线程.多进程的方式,运行代码展示异步与同步的区别. import gevent import threading import multiprocessing # 这里展示同步和异 ...

  8. oracle分区表编程,Oracle分区表详解

    当前位置:我的异常网» 编程 » Oracle分区表详解 Oracle分区表详解 www.myexceptions.net  网友分享于:2013-10-28  浏览:25次 Oracle分区表详解 ...

  9. java 可见性_Java并发编程-volatile可见性详解

    前言 要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸.最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍. 有什么 ...

最新文章

  1. 人工智能领导世界走向何方?
  2. 层次分解位置编码,让BERT可以处理超长文本
  3. 嵌入式学习在linux下使用c语言对文件进行打开、读写操作。_2000字干货!单片机进阶嵌入式Linux要怎么学习……...
  4. 使用JUnit 5测试异常
  5. vue 在 html 中自定义 tag
  6. matlab uigetfile的用法,matlab中uigetfile的用法
  7. python数据结构与算法篇:排序
  8. 【代码模板】不存在的NOIP2016
  9. 【noip2013】d2解题报告
  10. mini usb 接口定义 5针usb接口定义图
  11. 明解c语言实践篇翻译_《明解C语言》PDF版本下载
  12. 怎么禁用计算机的文件共享,怎么关闭掉电脑共享文件
  13. ai背景合成_AI突破次元壁又火了!飞屋环游记动漫角色一秒变真人,网友:小罗的“猫王发型”有点酷...
  14. 贡献一个fisco-bcos-browser-front基于官方的代码改造的兼容手机浏览器和pc浏览器
  15. IOS_3DTouch实现
  16. [ECE]模拟试题-5
  17. BUGKU_WEB_never give up
  18. 前端HTML5面试题
  19. JavaWeb学习之BS/CS架构及tomcat容器项目部署
  20. 企业经营(数据)分析

热门文章

  1. 解决dpkg/apt-get error : 子进程 post-installation script 返回了错误号 1
  2. 前端工程师扭伤了,也不忘背单词!
  3. python奇数阶乘求和_使用Python阶乘求和的方法及问题总结
  4. LuaStudio+ 一款移动端的应用开发工具
  5. Swift版知乎日报
  6. libRTMP使用说明
  7. 标准输入、标准输出究竟是什么?
  8. 互联网电视变阵:为何乐视屡遭内容滑铁卢,小米却逆袭成内容之王?
  9. 【动态规划 状压dp】JZOJ_6316 djq的朋友圈
  10. android多媒体框架介绍(四)显示图形系统之hwc叠加器