赞
踩
postman
通过网络给服务器要数据或把数据扔给服务器。
URL(全称是UniformResourceLocator)中文叫统一资源定位符,用于标识互联网上每个资源的唯一存放位置。浏览器只有通过URL地址,才能正确定位资源的存放位置,从而成功访问到对应的资源。
常见的URL举例:
http://www.baidu.com
http://www.taobao.com
使用 Ajax 请求数据时,被请求的 URL 地址,就叫做数据接口(简称接口)。同时,每个接口必须有请求方式。
**接口测试工具:**postman等。
接口文档:
接口文档,顾名思义就是接口的说明文档,它是我们调用接口的依据。好的接口文档包含了对接口URL,参数以及输出内容的说明,我们参照接口文档就能方便的知道接口的作用,以及接口如何进行调用。
接口文档包含的几个部分:
接口文档可以包含很多信息,也可以按需进行精简,不过,一个合格的接口文档,应该包含以下6项内容,从而为接口的调用提供依据:
接口名称: 用来标识各个接口的简单说明,如登录接口,获取图书列表接口等。
接口URL: 接口的调用地址。
调用方式: 接口的调用方式,如 GET 或 POST。
参数格式: 接口需要传递的参数,每个参数必须包含参数名称、参数类型、是否必选、参数说明这4项内容。
响应格式: 接口的返回值的详细描述,一般包含数据名称、数据类型、说明3项内容。
返回示例(可选): 通过对象的形式,例举服务器返回数据的结构。
中文的说法:阿贾克斯
Ajax:Asynchronous javascript and xml,异步的javascript和xml。
是指一种创建交互式网页应用的网页开发技术。
Ajax只是一门综合性的技术,本身是老技术,新应用。
这些技术就包括了:
服务端:服务端的语言(php、node),数据库(mysql、mongodb)
浏览器端:HTML、CSS、JavaScript、DOM
连接:HTTP、XMLHttpRquest对象
要全面掌握ajax,必须有熟悉前端和后端的知识。
请问:ajax到底是前端程序员的活还是后端程序的活?
可以站在两个角度来看待这个问题
提升用户体验 ---- 早期
前后端分离开发模式 — 现在
在软件开发领域中,只有两种架构:
C/S,client/server,在用户的电脑必须要安装特定的客户端软件,比如qq
B/S,browser/server,用浏览器就可以使用服务。
B/S | C/S | 备注 | |
---|---|---|---|
可维护性/更新 | 非常方便 | 比较麻烦,每个用户都需要更新客户端软件 | 既包括软件提供方,也包括用户 |
性能 | 胖服务端/瘦客户端,性能较差 | 胖客户端/瘦服务端 将一部分的功能放到用户的客户端上,性能更好 | 针对软件提供方 |
用户体验 | 差 | 好 | 针对用户 |
其实,可维护性和性能,都会对用户体验有影响。
B/S和C/S,相比较而言,用户体验是很差的,最重要的体现就是在响应的速度方面。
所以,既有具备B/S的优势,同时又希望能够具备C/S模式的的快速响应体验。
由于http的请求/响应模型,和服务端挂钩的任何一次用户的操作,都是一个全新的请求,会导致整个页面刷新了,从而大大降低了响应速度。
比如注册表单:
Ajax的出现,就可以让B/S架构能够具备C/S架构的快速响应能力。它是通过局部刷新(异步刷新)来实现的。
传统的开发模式:
前后端分离的开发模式:
在新的开发模式下,前端向后端要数据,就只能使用ajax的方式。
在今天的前端开发中,ajax的作用:
提升用户体验
实现前后端分离开发
2005年2月,Adaptive Path公司的Jesse James Garrett最早提出这个概念。它出现在Garrett的文章“Ajax: A New Approach to Web Applications”中。这篇文章描述了混合使用XHTML、CSS、JavaScript、DOM、XMLHttpRequest进行Web开发将会成为一种新的趋势。
同年,Google在三大产品中使用了Ajax技术:
Google Suggest
Google Maps
Gmail
ajax变迁史
传统的Ajax — XMLHttpRequest
新的ajax方式 — fetch/axios
反向Ajax
XMLHttpRequest是一套可以在Javascript语言中通过http协议传送或接收XML及其他数据的一套API。
XMLHttpRequest,简称为XHR对象,本质上就是一个对象。
可以从两个方面来看:
XHR对象所处的位置
XHR对象有哪些属性和方法
Ajax是一系列技术的综合运用。它包括浏览器端和服务端的。
XHR本身是在浏览器端的,是js中对象。
XHR对象,可以向服务端发送http请求,并且处理服务端返回的响应。
XHR相当于是一个通道,利用这个通道,浏览器和服务端可以悄悄的通信。
重要属性有:
重要方法有:
XMLHttpRequest 对象用于在后台与服务器交换数据。(这个后台是指悄悄的)
XMLHttpRequest 对象是开发者的梦想,因为您能够:
在不重新加载页面的情况下更新网页 (股票信息)
在页面已加载后从服务器请求数据 (异步,懒加载)
在页面已加载后从服务器接收数据
在后台向服务器发送数据
目标:检测用户名是否可用(类似于京东的用户注册)
需要实现两个版本:
传统的检测方式
利用ajax的检测方式
第一步,准备一个注册的表单页面
如下:
第二步,安装express和ejs
安装如下:
第三步,编写代码载入注册页面
编写代码如下:
启动,访问如下:
第四步,当用户提交表单时,检测用户名是否可用
注意,action为空或#,就表示提交给当前路径。
先安装body-parser中间件,如下:
编写代码如下:
测试,如下:
从功能的角度来讲,这个是可以的。
但是从用户体验来将,这个是非常糟糕的。
在这个检测中,当点击提交之后,如下:
页面发生了跳转,是一个全新的请求。
如果用户名已经被占用,就需要退回到注册页面,又是一个页面刷新,有很多信息已经重置了,再次填写一遍,所以这个体验是非常糟糕的。
A.分析过程
在传统的检测方式中,是将所有的信息填写完毕,然后点击提交才开始检测的。
用户希望的方式是一旦用户名填写完毕,就立即检测告诉我结果。
这就需要我们在用户名填写完毕的时候,向服务端发一个http请求,然后返回是否可用的结果。
所以,需要有两个点:
什么时机发http请求
如何发http请求并接受返回结果
什么时机发http请求
在市区焦点的时候,发请求,就是blur事件。
如何发http请求并接受返回结果
此时,就需要使用ajax,应该使用xhr对象来实现。
Ajax的实现过程:
创建xhr对象
建立和服务端的连接
监听服务端的状态变化
发送请求
B.具体实现
第一步,准备表单页面
第二步,提供server,载入静态页面
编写代码如下:
启动服务,访问如下:
第三步,需要注册blur事件,发起ajax请求
在reg2.ejs中,来编写代码,如下:
可以先测试一下:
说明,http请求已经发送了。
第四步,在服务端处理/check请求
重启,测试,如下:
有了ajax之后,用户体验大大提升。
在使用ajax之后,http请求/响应是有的,但是整个页面并没有刷新,这就是异步刷新(局部刷新)技术。
Ajax的核心,是XHR对象,所有的过程都是由XHR对象来实现的。XHR对象是重点
在进行ajax请求的时候,一定要结合network面板分析http请求
Network面板中,有一项XHR,就是专门针对ajax请求的
在标准的浏览器中,使用XMLHttpRequest构造器来new 即可。
输出如下:
需要注意的是:在ie6/ie7浏览器,是不支持这种写法的。(了解即可)
需要使用activeXObject对象来创建。
比如:
var xhr = new ActiveXObject(“Microsoft.XMLHTTP”); //最原始方式 ie5/ie6
var xhr = new ActiveXObject(“Msxml2.XMLHTTP”); //升级 ie7及以上
不需要自己来写代码创建,原因有二:
ie6/7已经很老了,可以不予支持
如果需要支持ie6/ie7,请使用jQuery
一旦创建好了xhr对象,就需要建立和服务端的连接。
需要使用open方法,格式如下:
xhr.open(请求方式,url,async)
其中,请求方式,有get和post两种。
Get和post都是在浏览器端向服务端发http请求的。
在发送http请求的时候,可以携带数据,针对数据的大小、形式、安全度是有所不同的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OearihBW-1597495967203)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image054.png)]
格式为:xhr.open(‘get’,url)
其中,
这个get是字符串,大小写都可以的。
url表示请求的地址,通常是带有查询字符串的。
如下:
针对get请求,需要发送,使用xhr.send来实现的。
send是用来发送请求正文的。只有调用send方法,才说明请求发送完毕的。
由于,get请求的请求正文是空的,所以可以不写任何内容,但一般使用null来表示。
针对get请求,需要注意一些特殊符号:
对中文 (ie浏览器会有乱码)
=
&
= 和 & 本身是查询字符串中的符号,会有问题。
如下:
Ie的中文问题
所以,千万不要相信用户的输入,凡是用户输入的内容,都需要进行处理。
此处,必须要进行编码 ---- encodeURIComponent
使用如下:
再次访问:
编码的结果如下:
针对编码,我们无需解码,浏览器会自动解码。
针对post请求,有如下几个地方:
xhr.open(“post”,url)
需要使用setRequestHeader()把传递的数据组织为xml格式
调用send()方法时,需要传递数据,作为请求正文
特殊的符号(=和&)、中文需要编码
Post请求的同时也可以使用查询字符串来传递数据
使用如下:
对应的,在服务端,需要使用post方式来处理请求,如下:
重启,测试,如下:
在开发过程中,get方式更为常见(占90%),post用的比较少。
get,主要是用于从服务端获取数据的。
post,主要是用于向服务端添加数据的。
URL 地址中,只允许出现英文相关的字母、标点符号、数字,因此,在 URL 地址中不允许出现中文字符。
如果 URL 中需要包含中文这样的字符,则必须对中文字符进行编码(转义)。
**URL编码的原则:**使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。
URL编码原则的通俗理解:使用英文字符去表示非英文字符。
如何对URL进行编码与解码?
浏览器提供了 URL 编码与解码的 API,分别是:
encodeURI() 编码的函数
decodeURI() 解码的函数
由于浏览器会自动对 URL 地址进行编码操作,因此,大多数情况下,程序员不需要关心 URL 地址的编码与解码操作。
参考:https://blog.csdn.net/Lxd_0111/article/details/78028889
把数据对象转换为字符串的过程,叫做序列化,例如:调用 JSON.stringify() 函数的操作,叫做 JSON 序列化。
把字符串转换为数据对象的过程,叫做反序列化,例如:调用 JSON.parse() 函数的操作,叫做 JSON 反序列化。
Ajax 操作往往用来提交表单数据。为了方便表单处理,HTML5 新增了一个 FormData 对象,可以模拟表单操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRDMAN28-1597495967226)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image078.jpg)]
FormData对象也可以用来获取网页表单的值:
同步和异步主要是指代码的执行顺序问题。
同步,按照代码书写的顺序依次执行,代码的执行顺序和代码书写的顺序是一致的。
异步,代码执行的顺序和代码书写的顺序是不一致的。
由于js是单线程的,一次只能处理一个任务。如果某个任务,耗时比较长,后续的任务就需要等待。
为了解决同步的任务等待,所以提出了异步的解决方案。
所谓异步,将某些耗时较长的任务使用异步的方式来处理。这样一来,就将所有的同步任务先执行完毕,然后再执行异步任务,从而提高整体的效率。
在ajax中,如果是同步的请求,那么在结果没有返回之前,你就处于等待状态,不能进行其他操作。
如果是异步的请求,那么在结果返回之前,还可以进行其他的操作。无需等待。
通过open方法的第三个参数设置异步或同步,默认true,表示异步。
如果设置为false,就表示同步。
在使用异步的时候,如下:
如果是同步,如下:
在实际使用时,异步肯定使用较多。
什么情况下需要使用同步?
如果后续的请求,需要使用ajax请求的结果,可以使用同步。
在浏览器端的js中,异步有如下三个地方:
setTimeout和setInterval
dom中的事件
Ajax
如果是服务端的js,异步有:
在ajax的使用过程中,有如下一段代码:
涉及到了四个属性:
onreadystatechange
readyState
status
responseText
其中,onreadystatechange、readyState和status是和状态有关。
而responseText则是指返回的数据。
Xhr对象,本身有一个属性—readyState,用来描述xhr的状态的。
ReadyState有如下5个值,分别表示不同的阶段:
加入调试代码:
查看结果:
在0~4这5个状态中,其中:
0表示xhr对象已经创建好了
1则表示已经向服务端发送请求了。
这两个值,咱们不需要。
请求一旦发出去,我们比较关心的是服务端的返回情况。服务端到底有没有把数据返回并解析完成。
服务端返回数据并解析的过程,不是一下子就完成,分成了3个阶段,分别对应于3个状态,只有当状态值变为4的时候,才表示解析ok。
请求,我们如何来捕捉这个状态的变化。
此时,就应该使用事件机制,在xhr对象中,就提供了一个事件–readystatechange,用于监听readyState的变化,换言之,请求发送之后,只要readyState发生变化(不包括0和1),就会触发readystatechange事件。
readyStatechange事件是作为onreadystatchchange属性来注册的。
所以,我们需要注册readystatechange事件,如下:
这个事件处理函数,至少会执行3次(状态有可能会执行多次)。
而我们需要在readyState = 4的状态才能获取数据。
所以,就需要增加一个判断,xhr.readyState == 4
除readyState之外,xhr还提供了一个属性 — status,用于获取http响应的状态码。
典型的状态码有如下几个:
200,ok
302,founded
304,not modified
403,forbidden
404,not found
500,服务器内部错误
所以,需要增加一个判断,xhr.status == 200,如下:
注意:onreadystatechange是全小写,而readyState则是小驼峰命名法则
在xhr中,针对返回数据的格式,它提供了两个属性:
responseXML
responseText
这个是指数据以XML的格式从服务端返回到浏览器端。
实际上,早期都是使用XML格式的。
在今天,XML这种格式以及非常落后的,在ajax中,几乎不再使用XML这种格式。
因为,XML数据不够轻量,处理起来比较麻烦。
实际上Ajax中的x,有点名不副实。
实际上,我们在开发的时候,只需要使用responseText就可以。
responseText是指数据以文本字符串的格式从服务端返回到浏览器端。
大家需要明白:数据在浏览器端和服务端进行传输的过程中,都是以文本字符串的形式来进行的。
尽管responseText是字符串,但是也有不同的表现形式:
普遍的纯文本
html字符串
Json字符串
它是针对,我们在服务端返回字符串的表现形式。
A.普遍的纯文本
这种最简单,就是简单的纯文本。
比如:
在xhr中,直接获取,如下:
拿到之后,根据实际情况来处理。如:
B.html字符串
通常是需要在服务端,返回一段拼接好的html字符串。
如:
针对html字符串,我们在浏览器端拿到之后,一般都是使用innerHTML写入DOM,如下:
效果如下:
C.Json字符串(重点)
Json格式,使用最多的。
请问json是什么?是对象 or 字符串?
是字符串,不是对象。只不过字符串长了一张对象的脸。
两个方面都需要注意:
服务端返回json格式的字符串
浏览器端需要获取,然后将其转成对象
服务端返回json格式的字符串
如下:
浏览器端需要获取,然后将其转成对象
先获取,并打印:
输出如下:
所以,需要对字符串,进行转换,如下:
输出如下:
转成对象之后,使用就方便了。
针对服务端的对象转json字符串,可以用两种简化方式:
直接使用send
使用json
直接使用send,如下:
实际上,send它会自己去判断参数的格式,然后会写入相应的响应头,如下:
换言之,send方法会自动将对象转成json字符串,然后传输。
直接使用json方法,如下:
Xhr对象,作用就是用于在浏览器端向服务端悄悄的发送一个http请求,并处理服务端返回的结果,从而实现异步(局部)刷新。
使用思维导图进行小结,如下:
在使用xhr的时候,一定要结合network面板来查看请求的详情。
针对当前效果,有哪些特点,如何实现呢?
首先,整个页面并没有刷新,但是我们确实是获取到了相应的数据。
肯定需要使用ajax来实现。
分析发ajax请求的时机:
页面载入完毕(load)
切换省份的下拉列表中的数据(change)
切换市区的下拉列表中的数据(change)
请问,省市区的数据从何而来呢?
一般而言,数据肯定是存放到数据库中的。
针对一些特殊的数据,我们也可以采取其他的保存方式,比如使用json文件。
如果某个数据,具备如下两个特点:
数据量比较小
数据比较固定的,不会变化
此时,完全可以使用json文件来保存,更加简单和灵活。
第一步,需要准备一个静态页面
第二步,需要提供一个http server,载入静态页面
启动服务,访问如下:
第一步,在浏览端,发起ajxa请求:
运行,如下:
说明,请求已经发出了。
第二步,在服务端,处理/provnice的请求
重启,测试,如下:
第三步,将获取的省份写入select中
编写代码如下:
查看结果,如下:
技巧:如果想查看本地的josn文件格式,使用firefox浏览器。如下:
当用户点击省份的下拉列表,切换省份时,需要发起ajax,获取对应的市区数据。
第一步,注册chang事件,发ajax请求
编写代码如下:
需要测试,如下:
第二步,需要在服务端处理/city的请求
编写代码如下:
测试,如下:
说明,ok。
第三步,在浏览端,将返回的结果写入select
编写代码如下:
测试,ok,如下:
当用户切换市区select中的市区时,需要载入对应的区县。
第一步,注册chang事件,发ajax请求
测试,如下:
说明,请求已经发送了。
第二步,在服务端需要处理/country,返回数据
此时,需要注意数据的结构,如下:
编写代码如下:
测试一下,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DpMUjqrj-1597495967341)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image189.jpg)]
由于直辖市,是没有三级数据的,如图:
所以,我们需要进行一个判断,修改代码如下:
再次测试,如下:
说明,ok。
第三步,在浏览器将结果获取到,写入到select中
编写代码如下:
测试,如下:
最后整体测试,如果已经有了一个三级的区县数据,然后再切换省份的时候,三级的数据仍然还在,如下:
这个是不对的,需要完善一下。
实际上,在切换省份数据的时候,需要清空区县的下拉列表。修改如下:
再次测试,ok。
任务:
检测用户名是否可用
省市区三级联动
可以回顾一下,前面我们使用xhr对象来实现ajax 的步骤:
创建xhr对象
建立连接
监听状态变化
发送请求
在具体实现的时候,还有一些细节:
针对特殊的符号(&和=)及中文,需要编码
Post请求,需要额外的发送一个头,使用setRequestHeader
在ie6/7中,创建xhr对象需要使用AxtiveXObject对象。存在兼容性问题
通过分析,我们发现,使用原生的方式来实现ajax,比较麻烦。在开发中,ajax又是非常常用的技术。为了实现快速高效的开发,通常需要我们对ajax进行封装。
其实,各大优秀的js库,都对ajax进行了封装。比如jquery、prototype、moontools等都提供相应的封装。
重点需要掌握如下三个即可:
$.get()
$.post()
$.ajax()
$.get(url, [data], [callback] ,type),GET方式异步请求
$.post(url, [data], [callback] ,type),POST方式异步请求
参数说明如下:
提醒:智能猜测不可靠,必须要自己写上。
第一步,先准备静态页面
第二步,提供http server,载入表单页面
启动,如下:
第三步,需要引入jquery
首先,创建public目录,将jquery放到其js目录之下,如下:
需要使用static中间件托管静态资源,如下:
第四步,编写代码,发ajax请求
测试,查看请求,如下:
第五步,在服务端处理/check请求,发回json数据
编写代码如下:
重启服务,测试,ok。
第一步,准备静态页面,如下:
第二步,载入静态页面
第三步,使用$.post发ajax请求
第四步,在服务端处理/check请求
重启服务,测试,如下:
. g e t 和 .get和 .get和.post都是异步方式
方法名必须是小写。(js严格区分大小写)
这里的get()和post()是一个全局方法,可以理解为静态方法
callback指定的回调函数只有在请求成功时才会执行,如果请求失败(例如找不到页面、服务器错误等)则不作任何处理。
用法一:$.ajax( [ settings ] ] )
用法二:$.ajax( url [, settings ] ] ) 1.5新增
具体的参数有如下:
url,请求的目标URL,默认为当前页面
async,是否是异步请求,默认为true,异步
Type,请求类型,可以为POST或GET,默认为GET
data,发送到服务器的数据,可以是字符串或对象类型
dataType,指定返回数据类型xml/html/script/json/text/jsonp
success,指定请求成功后执行的回调函数。
error,请求失败时执行的回调函数
timeout,设置请求超时的毫秒值
第一步,准备静态页面
第二步,提供http server,载入表单页面
第三步,使用$.ajax方法发请求,如下
测试,如下:
第四步,在服务端处理,如下:
Timeout参数,用于设定超时时间,如下:
效果如下:
还需要注意,$.ajax方法非常的强大,还有一些相对使用少一些的参数。
在遇到具体的需求时,需要学会快速的查阅手册。
实际上,在jQuery中,针对ajax的封装,$.ajax是核心方法。
其他的一些方法,比如 . g e t 、 .get、 .get、.post、 . g e t J S O N 、 .getJSON、 .getJSON、.getScript都是调用$.ajax方法的。
换言之, . g e t 、 .get、 .get、.post、 . g e t J S O N 、 .getJSON、 .getJSON、.getScript是$.ajax方法在特定场景下的快捷方式。
比如:
. g e t , 就 是 在 调 用 .get,就是在调用 .get,就是在调用.ajax方法时,固定了async为true,type为get,去掉error,保留success方法。如下:
针对jquery中封装的ajax功能,只需要掌握一个核心方法 — $.ajax
重要,很重要,非常重要。
衡量一个前端程序员的水平,可以看它是否掌握了ajax。
衡量一个前端er是否掌握了ajax,就看是否掌握了跨域。
如果你真的开发过项目,你必然会用到ajax,在进行ajax的开发,你必然会遇到跨域问题。
尤其是在前后端分离的开发模式中。
是指通过js在不同的域之间进行数据传输或通信,如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据。
三点:
针对不同的域,才会存在跨域问题。比如在localhost域名中,访问自己的,就不存在跨域。
必须是使用js的情况。比如直接在地址栏中输入url,不存在跨域。
必须是在浏览器端。比如在服务端,不存在跨域。
在一个完整的url中,跨域中的域,包括如下三个方面:
协议,如http/https
主机,有域名和ip两种,如www.baidu.com,127.0.0.1,
端口,如80,8080
如果这三者完全一致,就表示是同一个域,只要有一个不同,就认为是不同的域。
注意关于完全一致,有两个地方需要注意:
主机,就算域名和ip是对应关系,也被认为是不同的。
端口,端口如果省略了,和带端口的也被认为是不同的。如果端口都省略了,是相等的
条件非常的严苛。
再比如:
如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源。
同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。
MDN 官方给定的概念: 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
通俗的理解: 浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互,例如:
无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
无法接触非同源网页的 DOM
无法向非同源地址发送 Ajax 请求
什么是跨域:
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
第一步,准备静态页面
第二步,提供http server,载入静态页面
第三步,点击按钮时,发ajax请求
当点击button时,向卖座网要数据,
Url如下:http://m.maizuo.com/v4/api/film/now-playing?__t=1505879869719&page=1&count=5
请求,如下:
这怎么就跨域了?
使用js代码来请求的
js代码是浏览器端
是两个不同的域,请求方是http://localhost:4000/,被请求方是http://m.maizuo.com/v4/api/film
所以,刚好就触发了跨域的条件了。
在本地也可以模拟的。
如localhost:3000 --> localhost:4000 也是属于跨域。
基于安全的需要,在web开发中,是不允许跨域访问的。
有一个著名的安全策略 — 同源策略
同源策略的一个目的,就是为了安全起见。
在实际开发,我们又需要跨域去访问。
同源策略,到底是带来安全,还是带来了不便呢?
安全是第一位的,这个策略没有问题的。
但是,我们在开发的时候,有的时候是需要跨域的?
两大场景:
对于大型网站而言,有很多的子域/二级域名,需要恭喜某些数据,需要在不同的域之间请求数据。比如百度,https://www.baidu.com/、http://news.baidu.com/、http://music.baidu.com/、http://image.baidu.com/。
在前后端分离的项目中,将前端的项目和后端提供的api接口部署在不同的域名之下。(尤其是开发过程中)
关于跨域,有多大10余种解决方案。
主要是有如下三大方案:
Cors
代理
Jsonp(重点)
针对每一种解决方案,需要弄清楚:
使用场景
具体如何使用
CORS:出现的较晚,它是 W3C 标准,属于跨域 Ajax 请求的根本解决方案。支持 GET 和 POST 请求。缺点是不兼容某些低版本的浏览器。
CORS是cross origin resource share,跨域资源共享
我们看从跨域时报错信息来看,
说白了,就是localhost:4000域没有权限去异步请求m.maizuo.com/域。
作为m.maizuo.com/域这一方,我们可以去设置谁有权限来访问。
使用场景
针对cors,有前提条件,我们是有权限去编写/修改服务端的代码。或者说服务端的代码就在我们手上。
Cors这种方案,只需要在服务端开发访问权限即可。
在浏览器端不用写任何的代码。
第一步,准备一个静态页面
·
第二步,在localhost:3000提供http server,载入静态页面
启动,浏览,如下:
第三步,在html页面中,向localhost:4000发ajax请求
第四步,在localhost:4000端口提供http 服务,
启动4000端口的服务,再次访问,如下:
说明,有跨域限制。
在浏览器端使用js,进行如下的请求
http://localhost:3000/ ----> http://localhost:4000/ajax
第五步,在localhost:4000服务端开发权限
开发权限的代码是固定的,如下:
app.use("*",(req,res,next) => {
res.setHeader('Access-Control-Allow-Origin', "http://localhost:3000");
res.setHeader('Access-Control-Allow-Credentials', true); //允许携带cookie
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
next();
});
开放的域,可以使用*表示所有的域。
增加代码如下:
重启服务,访问如下:
说明,跨域成功了。
其中,允许的源,可以使用*表示所有的,如下:
对于一些公共的开放接口,比如天气预报、地图服务、ip查询、电话号码归属地查询等待,其实都是已经开发给所有用户来使用的。
和日常生活中的代理是一个意思,会涉及到第三方。
使用场景
没有限制,唯有一条,自己必须会一门后端语言。
所谓的代理,是指自己需要去写服务端的代码。
如图:
直接访问,是行不通的
增加一个代理层
需要注意一点,代理方和访问方需要在同一个域下。
第一步,准备静态页面
第二步,提供一个http server,载入页面
启动服务,点击,如下:
说明,有跨域限制。
第三步,增加一个代理层
在浏览器端,将url修改为当前域下的url,将目标地址作为参数传递过去,如下:
然后,在服务端,就需要处理/proxy的路由,这个路由中,需要获取传递过来的目标url,向其发起http请求,将结果获取到,并返回。
重启服务,再次请求,如下:
注意:代理方向目标域发http请求,存在跨域吗?
不存在,因为代理方是服务端的js代码。
思考:如果我不能(不会)代理给当前域?还可以使用代理方式吗?
JSONP**:**出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持 GET 请求,不支持 POST 请求。
重要程度:*****
Jsonp:json with packing的简写。
将json数据包装起来并返回的一种方式 ----- 协议
需要将jsonp和json区分开:
Json是字符串,是数据
Jsonp是协议,是传递json数据的方式
Json和jsonp的关系,好比是情报和传递情报的方式。
在使用jsonp实现跨域的时候,它是需要将json数据作为函数的参数(packing的过程),最终在服务端将包装好的字符串作为结果返回给浏览器端。
使用场景
在使用jsonp的时候,需要确保:
有权限去编写服务端的代码
在浏览器端也需要写一些代码
第一步,准备静态页面
第二步,提供一个http server,载入静态页面
浏览,请求,如下:
有跨域限制。
第三步,使用jsonp来实现跨域。
jQuery中的ajax方法,针对jsonp有一个封装。通过dataType来设置的。
只需要将dataType设置为jsonp,如下:
再次访问,如下:
说明有语法错误,这是咋回事呢?
需要通过network分析http协议,我们发现请求已经返回了相应的数据,如下:
分析,返回的结果是一个json字符串,不是包装好的jsonp协议的字符串。在浏览器端,它是按照jsonp的协议来解析这个结果,所以语法错误。
Jsonp返回的不能是json字符串,必须是函数调用的字符串。Json数据作为函数的参数。
如 f1( “{ a : 100, b : 200 }” )
这就需要我们在服务端,返回数据的时候,必须遵循jsonp的协议。
此时,就需要去编写服务端的代码。
要实现jsonp,关键是服务端,必须要以jsonp的协议返回数据。
在localhost:4000中提供一个http服务, 如下:
在浏览器中,向localhost:4000/ajax发请求,如下:
启动服务,再次请求,如下:
再次,说明,服务端返回的格式是错误,从而导致解析出错。
关键:服务端,必须要返回一个函数调用的字符串,json数据(字符串)作为函数的参数。
请问:
函数名是什么?
怎么获取的?
再一次使用network分析http,如下:
明明请求的路径是/ajax,我也没有传递任何的data,
实际上,是由于我们设置了dataType为jsonp,所以jQuery就自己随机了一个函数名,将其作为查询字符串传递给服务端了。
在服务端,通过查询字符串来获取即可。编写代码如下:
重启,测试,结果如下:
小结:
在使用jquery的ajax方法实现jsonp跨域,必须:
在浏览器端,设置dataType为jsonp
在服务端,必须以函数调用json字符串的形式返回作为字符串返回
在express中,针对jsonp格式的返回,专门封装了一个jsonp方法,如下:
这种使用jsonp方法时候,无需获取函数名,无需转换json,只需要调用jsonp方法,将json数据作为参数即可,如下:
测试,ok。
实际上,jsopn方法底层就实现了刚才的那两行代码。
由于jQuery封装好了jsonp的用法,导致很多初学者都错误的理解了jsonp。
实际上,jsonp方法并没有使用xhr对象来完成异步请求。查看network,如下:
Jsonp的实现是通过动态的添加script标签从而发送http请求来实现的。
实现如下
第一步,准备静态页面
第二步,在点击button的时候,需要发起异步请求
不能使用xhr来http请求,也不使用jquery封装的ajax相关方法。
编写代码如下:
第三步,在3000端口提供http server并托管静态资源
第四步,创建test.js,如下:
执行如下:
说明,这个请求是可以发出的。
第五步,使用addScript发一个普通的请求
只要是script标签的src,写任何地址都可以。能够返回结果并执行,则是另外一回事。
第六步,在服务端,处理/jsonp请求
在返回的时候,返回一段js代码,如下:
重启,执行,如下:
之所以,可以执行,那是因为,返回的是一段js代码,就好比是直接插入到如下位置:
第七步,需要在服务端返回相应的数据,在浏览端获取并操作
目前我们是直接在服务端输出js代码,在浏览器端我们就不能拿到返回的结果,进行二次处理。换言之,浏览器端是被动的执行js代码,而不是获取返回的结果,主动的去操作。
在浏览器定义一个函数,如下:
需要告诉服务端,我定义的函数叫什么名字?
如此一来,就可以在服务端获取这个函数的名字,如下:
测试,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORUBz5b7-1597495967509)(fil
实际上,在服务端返回的通常是json字符串,如下:
测试如下:
再次回顾jsonp的概念:
将json数据包装起来并返回的一种方式 ----- 协议
在使用jsonp实现跨域的时候,它是需要将json数据作为函数的参数(packing的过程),最终在服务端将包装好的字符串作为结果返回给浏览器端。
在jquery中的ajax方法中,和jsonp相关的还有如下两个设置:
默认如下:
可以自己设置,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DUs79JSq-1
测试如下:
但是,需要注意,res的jsonp方法,要求回调函数名必须是callback,否则出错,如下:
三种方式都已经讲解完毕。
在实际开发的时候,具体使用哪种方式,需要看情况来定。
作为前端,有时候是主动的,有时候是被动的。
而且,可以将cors和代理结合到一起,可以天下无敌。(大家自己写一写)
webpack是非常牛x的前端构建工具。
现在几乎所有的前端开发,都会用到webpack,尤其基于框架(vue、react、angular)的开发。
Webpack安装了一个第三方模块 – http-proxy-middleware,也可以实现跨域,只需配置即可。(后面补充)。
第一步,准备数据
使用insert命令插入到mongodb中,如下:
第二步,编写服务端代码,提供http server,获取数据,渲染到页面
第三步,在视图页面中,使用ejs模板语法输出数据即可
浏览,效果如下:
任务:
实现仿google suggest
三种跨域,都需要敲2~3遍
jsonp的原理,一定要多敲两遍,理解
不分页显示博客
分页显示、ajax分页都可以尝试着做
针对前面的数据展示方式,肯定是有问题的:
用户体验不好
性能有影响
针对大量的数据,通常需要使用分页的方式来展示。如:
所以,需要对我们的博客页面进行改造,添加分页功能。
从两个方面:
数据库查询
传递分页参数
A.从数据库查询的角度来分析
设定,每一页显示5条,使用pagesize表示,当前是第几页使用page表示
第一页,db.blog.find({},{_id:0}).limit(5).skip(0)
第二页,db.blog.find({},{_id:0}).limit(5).skip(5)
第三页,db.blog.find({},{_id:0}).limit(5).skip(10)
…
第n页,db.blog.find({},{_id:0}).limit(pagesize).skip((page - 1)* pagesize)
B.传递分页参数
通过上面的分析,我们需要获取的参数是当前页数。这个是变化的,和用户的操作相关。
默认是第一页,其它的页面,需要使用查询字符串来传递的
分两步实现:
手动传递参数,实现分页查询
提供分页信息及操作
第一步,提供一个http server,实现分页查询
比如第一页:http://localhost:3000
第二页:http://localhost:3000?page=2
第三页:http://localhost:3000?page=3
查询语句为:db.blog.find({},{_id:0}).limit(pagesize).skip((page - 1)* pagesize)
编写代码如下:
第二步,在视图页面中使用ejs来输出数据
这个视图和前面的那个是差不多的,如下:
启动服务,浏览如下:
针对page参数,需要做一些处理和限定,如下:
通常,需要在列表的底部,提供分页的信息及操作,如下:
第一步,先在页面中增加一个固定的分页信息,如下:
显示如下:
现在,需要将x、y、a和b变成活的。
第二步,在服务端获取这些值,传递到模板页面
我们已经有了当前页数,和每页显示的条数,就剩下总的记录数和总的页数。
关键是要获取总的记录数,需要查询数据库
需要再次查询数据库,如下:
第三步,在视图页面中输出分页信息,如下:
测试,如下:
第四步,实现上一页/下一页的跳转
在刚才代码中,增加一点点内容,如下:
在点击上一页/下一页的时候,查看时间的变化,
时间总是在变化,说明整个页面都进行了刷新操作。
在实际的web中,有很多时候,列表只是页面的一小部分,比如京东的商品详情页:
如果这个翻页操作是整体刷新,会怎么样?
用户体验下降
Web的性能受到影响
需要使用局部刷新技术来改造分页显示。
接下来,使用ajax来实现分页。
如果使用ajax方式,获取数据的方式发生了变化:
传统方式,在服务端查询,然后分配到模板页面
Ajax方式,在浏览器端发http请求,服务端返回,在浏览器端写dom即可
这就意味着,在ajax方式下,不需要使用ejs。
第一步,准备静态页面
第二步,提供一个http server,载入静态页面
启动服务,浏览,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOtaDj93-1597495967577)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image426.jpg)]
第三步,在浏览器端发起ajax请求,获取数据
在基于ajax的方式中,第一次显示的数据也是通过ajax来实现的。
浏览,查看xhr请求,如下:
说明请求已经发送了。
第四,在服务端处理请求,返回数据
这个和刚才的获取数据是一样的。
同时,需要修改一下浏览端的代码,如下:
启动,输出如下:
[
第五步,显示分页信息
修改分页信息的结构,如下:
在ajax返回结果的时候,写dom操作,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SS2sN4bg-1597495967599)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image440.jpg)]
显示,如下:
第六步,实现上一下/下一页操作
需要对超链接注册时间,发ajax请求即可。
增加id属性,如下:
编写代码如下:
测试,ok。
分析加载更多的特点:
意味着,只能前进,不能后退。
所以,在这种模式下,只能使用累加的方式,也就是:
默认显示第一页中的记录,比如5条
后续的点击加载更多,就将固定的记录追加到页面中
第一步,准备静态页面
第二步,提供一个http server,载入静态页面,如下:
启动,浏览如下:
第三步,在页面载入成功的时候,发ajax请求
测试,如下:
第四步,在服务端处理/blog请求
重启,测试,如下:
第五步,浏览器端处理返回的结果
注意,第一次是载入页面成功的时候,发ajax请求。
结果如下:
第六步,注册点击事件,发ajax请求
测试,ok。
但是这个效果不理想。需要处理细节。
在服务端模拟一个延时操作。
此时,我们应该提供一个视觉反馈。
测试,
还有一点,如果没有数据了,应该提示用户。增加代码如下:
测试,ok。
还需要完善一点,在正在加载的时候,不能发请求。有两种解决方案:
在发请求的过程中,禁用按钮
在在请求的过程中,允许点击,但是不发ajax请求
此处,采用第二种。增加一个开关,如下:
测试,ok。
在今天的web当中,分页显示已经普遍使用滚动加载。
和加载更多有很多相似之处。
第一次是页面载入成功时,就发一次ajax请求,获取数据写入页面。
后续的是在页面到达底部时,再发ajax请求,获取数据,追加到页面中的。
不同之处:
原来的方式,是在按钮上点击的时候,触发ajax请求
现在的方式,在页面滚动到底部的时候,触发ajax请求。
关键在于,如何知道 页面滚动到底部。
在滚动的场景中,有如下三个高度:
可视窗口的高度
文档的高度
滚动条卷起的高度(滚动的距离)
在jQ中,封装了如下方法:
滚动条滚动的高度— $(window).scrollTop()
窗口的可视高度 ---- $(window).height()
文档的高度---- $(document).height()
可以写测试页面,编写代码如下:
在滚动的时候,查看输出,如下:
条件就出来了。
st + ch == dh,就说明到达底部了。
第一步,准备静态页面
第二步,提供一个http server,载入静态页面
启动服务,浏览如下:
第三步,封装一个方法,用于实现ajax请求数据
编写代码如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WiczmUd8-1597495967654)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image495.jpg)]
第四步,在服务端需要处理/blog请求
测试,ok。
第五步,需要注册滚动事件,判断是否滚动到底部,并发ajax****请求
编写代码如下:
测试,ok。
第六步,需要添加视觉反馈,提供良好的用户体验。
先准备一张图片
在发请求的时候,需要显示这张图片,在加载完毕时需要隐藏这张图片。
节流函数。(自己加一下)
所谓的滚动加载,其实就是分页的高级形式,只是在展示层面有变化,其本质仍然是分页显示数据。
表单在网页中主要负责数据采集功能。HTML中的标签,就是用于采集用户输入的信息,并通过标签的提交操作,把采集到的信息提交到服务器端进行处理。
action 属性用来规定当提交表单时,向何处发送表单数据。
action 属性的值应该是后端提供的一个 URL 地址,这个 URL 地址专门负责接收表单提交过来的数据。
当 表单在未指定 action 属性值的情况下,action 的默认值为当前页面的 URL 地址。
注意:当提交表单后,页面会立即跳转到 action 属性指定的 URL 地址
target 属性用来规定在何处打开 action URL。
它的可选值有5个,默认情况下,target 的值是 _self,表示在相同的框架中打开 action
method 属性用来规定以何种方式把表单数据提交到 action URL。
它的可选值有两个,分别是 get 和 post。
默认情况下,method 的值为 get,表示通过URL地址的形式,把表单数据提交到 action URL。
注意:
get 方式适合用来提交少量的、简单的数据。
post 方式适合用来提交大量的、复杂的、或包含文件上传的数据。
在实际开发中, 表单的 post 提交方式用的最多,很少用 get。例如登录、注册、添加数据等表单操作,都需要使用 post 方式来提交表单。
注意:
在涉及到文件上传的操作时,必须将 enctype 的值设置为 multipart/form-data
如果表单的提交不涉及到文件上传操作,则直接将 enctype 的值设置为 application/x-www-form-urlencoded 即可!
通过点击 submit 按钮,触发表单提交的操作,从而使页面跳转到 action URL 的行为,叫做表单的同步提交。
表单同步提交的缺点:
如果使用表单提交数据,则会导致以下两个问题:
页面会发生跳转
页面之前的状态和数据会丢失
解决方案:表单只负责采集数据,Ajax 负责将数据提交到服务器。
通过字符串拼接的形式,来渲染UI结构。
如果UI结构比较复杂,则拼接字符串的时候需要格外注意引号之前的嵌套。且一旦需求发生变化,修改起来也非常麻烦。
模板引擎,顾名思义,它可以根据程序员指定的模板结构和数据,自动生成一个完整的HTML页面。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FX5cNxWV-1597495967669)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image511.jpg)]
模板引擎的好处
减少了字符串的拼接操作
使代码结构更清晰
使代码更易于阅读与维护
art-template 是一个简约、超快的模板引擎。中文官网首页为:
http://aui.github.io/art-template/zh-cn/index.html
导入 art-template
定义数据
定义模板
调用 template 函数
渲染HTML结构
标准语法:
art-template 提供了 {{ }} 这种语法格式,在 {{ }} 内可以进行变量输出,或循环数组等操作,这种 {{ }} 语法在 art-template 中被称为标准语法。
标准语法之输出:
{{value}}
{{obj.key}}
{{obj[‘key’]}}
{{a ? b : c}}
{{a || b}}
{{a + b}}
在 {{ }} 语法中,可以进行变量的输出、对象属性的输出、三元表达式输出、逻辑或输出、加减乘除等表达式输出。
标准语法之原文输出:
{{@ value }}
如果要输出的 value 值中,包含了 HTML 标签结构,则需要使用原文输出语法,才能保证 HTML 标签被正常渲染。
标准语法之条件输出:
如果要实现条件输出,则可以在 {{ }} 中使用 if … else if … /if 的方式,进行按需输出。
{{if value}} 按需输出的内容 {{/if}}
{{if v1}} 按需输出的内容 {{else if v2}} 按需输出的内容 {{/if}}
标准语法之循环输出:
如果要实现循环输出,则可以在 {{ }} 内,通过 each 语法循环数组,当前循环的索引使用 $index 进行访问,当前的循环项使用 $value 进行访问。
{{each arr}}
{{KaTeX parse error: Expected 'EOF', got '}' at position 6: index}̲} {{value}}
{{/each}}
标准语法之过滤器:
过滤器的本质,就是一个 function 处理函数。
{{value | filterName}}
过滤器语法类似管道操作符,它的上一个输出作为下一个输入。
定义过滤器的基本语法如下:
template.defaults.imports.filterName = function(value){/return处理的结果/}
template.defaults.imports.dateFormat = function(date) {
var y = date.getFullYear()
var m = date.getMonth() + 1
var d = date.getDate()
return y + ‘-’ + m + ‘-’ + d // 注意,过滤器最后一定要 return 一个值
}
由于近几年来,前端的开发方式发生了变化。
主要是体现:
前后端分离
mv*框架的大力发展
比如,现在典型的前端,需要掌握vue.js/recat.js/angular等框架来进行开发。
针对vue.js/react.js/angular,有如下特点:
提倡数据驱动DOM
遵循前后端分离开发思想
在数据驱动DOM的开发模式中,我们再也不需要进行dom操作了。只需要去设置状态即可,界面会根据状态来进行相应的显示(本质是dom操作由框架本身来实现的)。因此,在使用vue.js/react.js/angular进行开发的时候,就不再需要jQuery了。
在前后端分离开发思想中,我们作为前端,必须要使用ajax****方式去异步获取数据并渲染。
在使用ajax的适合,我们有如下两种选择:
使用原生的xhr对象来实现
使用jQuery封装的ajax相关方法
如果使用原生的xhr对象来实现,需要编写复杂的代码,还需要考虑浏览器的兼容性,注意细节等,对程序员不友好。
如果使用jQuery封装的ajax相关方法,完全可以避免原生方式的缺点。
我们只是为了使用ajax,然后引入了一个巨大的jQuery,有点不划算。对性能有所损失。对用户不友好。
并且,这两种方式,功能还不够强大。
因此,针对ajax,就出现了一些新的方式:
+fetch
Fetch,是被誉为下一代ajax的标准,在不久的将来,就会取代XMLHttpReqeust对象。
Axios,则是针对ajax进行的一个高度封装的库,非常小,功能比较完善。
新的方式,只专注于ajax,没有其他功能,可以在任何项目中来使用。
Fetch,是被誉为下一代ajax的标准,在不久的将来,就会取代XMLHttpReqeust对象。
http://caniuse.com/#search=fetch
大部分浏览器本身就支持。
在实际开发时,可以安装一些相应的fetch库。
Github地址:https://github.com/github/fetch
使用非常的简单,基本的api,如下:
使用fetch,获取本地的数据,
在本地启动一个http server,在4000端口,提供一个服务,
编写代码,获取数据,如下:
测试,如下:
如果没有设置cors跨域,那么我们在使用fetch的时候,也会有跨域问题。
当前的文件的url是:
file:///E:/web07/ajax/code/37.%E4%BD%BF%E7%94%A8fetch%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE.html
目标的ulr是:http://localhost:4000/ajax
协议就不相同,所以也是跨域。
注意,浏览器有缓存,所以在开发的时候,有时候需要清除缓存。
可以使用强制刷新,ctrl + f5
Github地址:https://github.com/mzabriskie/axios
中文手册:https://www.kancloud.cn/yunye/axios/234845
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和node.js 中。
Axios既可以在浏览器端使用,也可以在服务端来使用。
特性:
从浏览器中创建 XMLHttpRequests
从 node.js 创建 http 请求
支持 Promise API
拦截请求和响应
转换请求数据和响应数据
取消请求
自动转换 JSON 数据
客户端支持防御 XSRF
支持度:
安装有如下两种方式:
使用script的方式引入
基于模块化的开发,使用npm方式安装并引入
Api:
还是获取localhost:4000/ajax的数据,如下:
测试,如下:
看本质,还是xhr请求,如下:
针对ajax开发,我们拥有了如下四种方案:
原生xhr
使用jQ封装的ajax方法
Fetch
Axios
在开发的时候,原生xhr对象基本不用。
其余三种基本上都会碰到
如果是传统的前端开发,一般都会使用jQ封装的ajax方法。
如果是基于vue.js开发,一般会axios,也可以使用fetch。
如果是基于react.js开发,一般会使用fetch,也可以使用axios
如果是基于angular开发,自己有封装好的http方法。当然也可以使用fetch和axios。
不管是哪一种方式,都会碰到跨域问题,解决的方案有如下几种:
cors
代理
Jsonp
Chrome浏览器的cors插件
Webpack的配置
本意是承诺。
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了promise对象。
所谓promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
如何理解:
没有异步就不需要promise。
Promise本身不是异步,只是我们去编写异步代码的一种方式
在学习promise的时候,一定要站在两个角度看待promise:
站在使用者的角度
站在定义者的角度
在如下几个地方,都必须使用promise的写法:
Fetch
Axios
典型写法:
特点是:
then方法通常是表示异步操作成功时的回调,catch方法通常是表示异步操作失败时的回调
在调用的时候then在前后,catch在后
then方法可以调用多次,前一个then的返回值,会作为后一个then的参数。
支持链式调用
其它的一些异步操作,我们也可以使用promise的用法。
比如,jquery中的ajax方法,也支持promise的用法,如下:
注意:和fetch/axios不同的是,此处使用done方法来替代then方法。
再次声明:
没有异步,就不需要promise
Promise本身不是异步的,只是编写异步代码的一种优雅方式。
需要来看一段典型的异步代码 — 异步读取多个文件
首先,创建4个文本文件,如下:
编写代码,使用node的fs模块来读取文件,如下:
功能没有任何问题,但是这个写法比较讨厌— 这就是臭名昭著的回调地狱(callback hell)。
针对这个写法,非常的糟糕,需要有一种机制,让这个写法变得更加优雅一些。
所以,在前端社区中,就有一些牛人,纷纷提供了一些新的写法,以避免回调地狱。
后来,es6就在这些基础之上,将Promise直接纳入的规范。从而成为es6中的标准用法。
由于promise本身出自于民间,就有很多不同的实现版本。
Es6将promise纳入自己规范的时候,也遵循了一个相应的标准 – Promise A+规范。
将其归纳为4321规范。
4:4大术语
3:3种状态
2:2种事件
1:1个对象
一定要结合异步操作来理解。
既然是异步,这个操作需要有个等待的过程,从操作开始,到获取结果,有一个过程的。
解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
拒绝(reject):指一个 promise 失败时进行的一系列操作。
终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
据因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
在异步操作中,当操作发出时,需要处于等待状态。
当操作完成时,就有相应的结果,结果有两种:
成功了
失败了
一共是3种状态,如下:
等待态(Pending)
执行态(Fulfilled)
拒绝态(Rejected)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bGvneU21-1597495967703)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image553.jpg)]
对每一种状态,有一些规范:
等待态(Pending)
处于等待态时,promise 需满足以下条件:
执行态(Fulfilled)
处于执行态时,promise 需满足以下条件:
不能迁移至其他任何状态
必须拥有一个不可变的终值
拒绝态(Rejected)
处于拒绝态时,promise 需满足以下条件:
不能迁移至其他任何状态
必须拥有一个不可变的据因
针对3种状态,只有如下两种转换方向:
pending --> fulfilled
pendeing --> rejected
在状态转换的时候,就会触发事件。
如果是pending --> fulfiied,就会触发onFulFilled事件
如果是pendeing --> rejected,就会触发onRejected事件
就是指promise对象
在es6中,本身提供了一个Promise的构造器,如下:
可以使用new 直接创建一个,如下:
执行,如下:
之所以报错,是因为这个Promise构造器,必须要传递一个参数。
构造器的参数,是一个回调函数,包含两个参数:
resolve
reject
编写如下:
再次查看,如下:
说明,我们已经创建好了Promise对象,当前这个对象处于 pending 状态。
回调函数中的两个参数,其作用就是用于转换状态:
resolve,将状态从pending --> fullFilled
reject,将状态从pending --> rejected
直接使用函数调用的方式来进行转换,在转换的时候必须要传递相应参数
Resolve函数的参数,就是指 终值(value)
Reject函数的参数,就是指 据因(reason)
编写代码如下:
查看如下:
其中,resolved 就是 fulFilled
查看,如下:
关于Promise的创建工作,已经完成了。
在创建promise对象,只需要根据需求,转换状态即可。无非就是调用两个函数:
resolve,传递value
reject,传递reason
比如,可以编写如下代码:
执行,如下:
思考,针对刚才的代码:
如何获取这里的终值 ok 或者 据因 error呢?
此时,就需要 利用 事件机制。
针对3种状态,只有如下两种转换方向:
pending --> fulfilled
pendeing --> rejected
在状态转换的时候,就会触发事件。
如果是pending --> fulfiied,就会触发onFulFilled事件
如果是pendeing --> rejected,就会触发onRejected事件
在调用resolve方法或者reject方法的时候,就一定会触发事件。
需要注册onFulFilled事件 和 onRejected事件
针对事件的注册,Promise对象提供了then方法,如下:
针对 onFulFilled,会自动提供一个参数,作为终值(value)
针对 onRejected,会自动提供一个参数,作为据因(reason)
注册事件,如下:
再次调用:
Promise主要是解决异步代码的编写方式。
案例1**:读取文件操作**
原来的写法,如下:
使用promise的写法:
案例2:根据随机数返回结果
执行,如下:
真正的promise****定义方式
回到读取文件案例,需要再读取b.txt,如何实现?
需要将读取文件的操作,封装为一个函数,如下:
在开发的时候,我们定义promise对象,必须是要作为函数的返回值。
任务:
分页的各种形式
可以使用iscroll插件、minirefresh 实现移动端的上拉加载,下拉刷新
Promise需要好好消化一下
Promise对象,有一个then,如下:
promise.then(onFulfilled, onRejected)
需要写两个回调。其中第二个回调表示 从pending —> rejected 时的回调。
由于这种写法,辨识度不高。
Promise就提供了一个catch方法,用于注册 onRejected回调。
使用如下:
在这里要明白两件事情:
所以,我们在使用promise对象时,一般这么描述,异步操作成功的时候,走then,失败的时候就走catch。
在fetch的代码中,有如下写法:
也就说,then方法后面继续的调用then方法,这就是then的链式调用。
编写代码如下:
重点是p2**,then方法返回的是一个promise对象,但它是一个新的promise对象,不再是原来的promise对象**,如下:
所以,then方法可以实现链式调用。
在then方法的链式调用中,有一个非常重要的特点:
前一个then方法的返回值,或作为下一个then方法的参数(普通参数,promise****对象例外)
如果返回的是promise对象,就作为下一次调用then方法的promise对象。
编写代码如下:
此时,就应该联想到在使用fetch的时候,如下代码:
注意:这个then方法中回调函数的return 返回值,和then****方法本身的返回值是两码事。
到这儿,我们就可以解决回调地狱的糟糕写法问题。
原来的写法,如下:
都是通过回调来实现的,层层嵌套,不优雅。
现在,我们就可以通过promise改写,使其使用同步的一种方式来编写代码。
如何理解:
这种写法,上一个then的返回值,就直接等于下一个then 的参数。
在这种写法中,返回的是一个promise对象,这个对象就作为下一次调用then方法promise对象。
最后,当有多个then方法调用的时候,需要将catch放到最后,从而捕获任何一个then方法的错误。如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-reYfLrkN-1597495967765)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image625.jpg)]
all:所有
race:竞赛
all和race都是Promise构造器对象的静态方法。直接使用Promise调用,如下:
Promise.all()
Promise.reace()
返回值都是promise对象。
当有多个异步操作的时候,经常会有如下两种需求:
确保所有的异步操作完成之后,才进行某个操作,只要有一个失败,就不进行
只要有一个异步操作文章,就里面执行某个操作。
有点类似于运算符中的 逻辑与 和 逻辑或。
all使用如下:
如果有错误,如下:
race****的用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJvqMR5K-1597495967770)(file:///C:/Users/17454/AppData/Local/Temp/msohtmlclip1/01/clip_image631.jpg)]
比如,在一个页面中,需要发多个ajax请求,必须保证所有的数据都返回,才显示页面,可以使用all。
又比如,异步获取一个数据,这个数据有多个接口,只要有一个成功即可,此时就可以race。
对开发中使用promise进行小结:
没有异步,就不需要promise。
不使用promise,其实也是可以解决异步编程的问题。使用promise,会使异步的编码变得更加优雅,功能会更强。
在进行promise编程的使用,有如下两个场景:
直接使用别人封装好的promise对象,比如fetch、axios
需要自己封装promise对象
注意:axios和fetch必须使用promise方式,如:
针对自己封装promise对象,又可以有如下两种方式:
自己封装
可以使用第三方的promise库
比如,针对第三方的promise库,有两个知名的库:
bluebird
q.js
可以利用bluebird 和 q.js 快速的生成promise对象。
以bluebird为例,在服务端演示其用法。
http://bluebirdjs.com/docs/getting-started.html
第一步,安装
第二步,使用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。