🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[toc] ## usage(4步) Ajax(async javascript and xml) 1)首先创建一个Ajax对象 ``` var xhr = new XMLHttpRequest; ``` 2)打开我们需要请求数据的那个文件地址 ``` xhr.open("get","./data.txt",true); //true为异步 ``` 3)监听请求的状态 ``` xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) { var val = xhr.responseText; data = utils.jsonParse(val); } }; ``` 4)发送请求 ``` xhr.send(null); ``` ![](https://box.kancloud.cn/493af7275d7e6e942ab5cff0ccb3d251_853x603.png) 设置请求头必须在open之后以及send之前 ![](https://box.kancloud.cn/450ddbfbe4ca58323d7c8b696c625736_915x306.png) ![](https://box.kancloud.cn/8863e610d2a617100d7ef48f83267054_752x601.png) ## xml 可扩展的标记语言 >作用是用来存储数据的(通过自己扩展的标记名清晰的展示出来数据结构) ![](https://box.kancloud.cn/dc622f30c83216cb3f8b27b38cb8f5d7_392x250.png) ajax之所以称之为异步的js和xml,主要原因是,当初最开始用ajax实现客户端和服务器端数据通信的时候,传输的数据格式一般都是xml格式的数据, 但现在我们一般都是基于JSON格来进行数据传输的 ## 异步的JS >这里的异步不是说ajax只能基于异步进行请求(虽然建议都是使用异步编程),这里的异步特制的是"局部刷新" ### 局部刷新VS全局刷新 ![](https://box.kancloud.cn/2c2576aca1469c7d39c6676b3128356e_1589x717.png) 在非完全前后端分离项目中,前端开发只需要完成页面的制作,并且把一些基础的人机交互效果使用JS完成即可,页面中需要动态呈现内容的部分都是交给后台开发工程师做数据的绑定和基于服务器进行渲染的。 优势: 1、 动态展示的数据在页面的源代码中可以看到,有利于SEO优化推广 2、从服务器端获取的结果就已经是最后要呈现的结果了,不需要客户端做额外的事情,所以页面加载速度快(前提是服务器端处理的速度够快,能够处理过来),所以类似于京东、淘宝这些网站,首屏数据一般都是经由服务器端渲染的 弊端: 1、如果页面中存在需要实时更新的数据,每一次想要展示最新的数据,页面都要重新的刷新一次,这样肯定不行 2、都交给服务器端做数据渲染,服务器端的压力太大,如果服务器处理不过来,页面呈现的速度更慢(所以京东淘宝这类网站,除了首屏是服务器端渲染,其它屏一般都是客户端做数据绑定) 3、 这种模式不利于开发(开发效率低) --- 目前市场上大部分项目都是前后端完全分离的项目 前后端完全分离的项目,页面中需要动态绑定的数据是交给客户端完成渲染的 1、想服务器端发送AJAX请求 2、把从服务器端获取的数据解析处理,拼接成我们需要展示的HTML字符串 3、把拼接好的字符串替换成页面中某一部分的内容(局部刷新),页面整体不需要重新架子啊,局部渲染即可 【优势】 1、我们可以根据需求任意修改页面中某一部分中的内容(例如实时刷新),整体页面不刷新,性能好,体验好(所有表单验证,需要实时刷新的等需求都要基于AJAX实现) 2、有利于开发,提高开发效率 1)前后端的完全分离,后台不需要考虑前端如何实现,前端也不需要考虑后台用什么技术,真正意义上实现了技术的划分 2)可以同时进行开发:项目开发开始,首先指定前后端数据交互的接口文档(文档中包含了,调取那个接口或则那些数据等协议规范),后台把接口先写好(目前很多公司也需要前端拿NODE来模拟这些接口),客户端按照接口调取即可,后台再次去实现接口功能即可。 【弊端】 1、不利于SERO优化:第一次从服务器端获取的内容不包含需要的动态绑定的数据,所以页面的源代码中没有这些内容,不利于SEO收录,后期通过JS添加到页面中的内容,并不会卸载页面的源代码中(是源代码,不是页面结构) 2、交由客户端渲染,首先需要把页面呈现,然后再通过JS的异步AJAX请求获取数据,然后数据绑定,浏览器在把动态增加的部分重新渲染(无形当中浪费了一些时间,没有服务器渲染页面呈现速度快) ## 基于原生JS实现AJAX ``` //=>创建一个AJAX对象 let xhr = new XMLHttpRequest(); //->不兼容IE6及更低版本浏览器 更低版本浏览器(IE6,ActiveXObject) //=>打开请求地址(可以理解为一些基础配置,但是还并没有发送请求) xhr.open([method],[url],[async],[username],[user password]) //=>监听AJAX状态,获取响应信息(获取响应头信息,获取响应主体信息) xhr.onreadystatechange = ()=>{ if(xhr.readyState===4&&xhr.status===200){ let result = xhr.responseText; //=>获取响应主体中的内容 } }; //=>发送AJAX请求(括号中传递的内容就是请求主体的内容) xhr.send(null); ``` ### 关于open >xhr.open([method],[url],[async],[user name],[user password]) > >[HTTP请求方式] >1、GET系列请求(获取) >- get >- delete:从服务器上删除某些资源文件 >- head:只想获取服务器返回的响应头信息(响应体内容不需要获取) >... >2、POST系列请求(推送) >- post >- put:向服务器中增加指定的资源文件(put一般比post还多,但没有特别明确的要求) >... > >不管哪一种请求方式,客户端都可以把一些信息传递给服务器,服务器也可以把信息返回给客户端,只是GET系列一般以获取为主(给的少,拿回来的多),而POST系列一般以推送为主(给的多,拿回来的少) >1)我们想要获取一些动态展示的信息,一把使用GET请求,因为只需要向服务器发送请求,告诉服务器端我们想要什么,服务器端就会把需要的数据返回 >2)在实现注册功能的时候,我们需要把客户端输入的信息发送给服务器进行存储,服务器端一般需要返回成功还是失败等状态,此时我们一般都是基于POST请求完成的 #### GET系列请求和POST系列请求,在项目实战中存在很多的区别 1、GET请求传递给服务器的内容一般没有POST请求传递给服务器的多 原因: GET请求传递给服务器的内容一般都是基于`url地址问号传参`来实现的,而POST请求一般都是基于`设置请求主体`来实现的。 各大浏览器都有自己URL最大长度限制(超过会被截取掉): 谷歌:8KB 火狐:7KB IE:2KB 2、 理论上POST请求通过请求主体传递是没有大小限制的 真实项目中为了保证传输的速率,我么也会限制大小(例如:上传的资料或则图片我们都会做大小限制) 3、GET请求很容易出现缓存(这个缓存不可控,浏览器自己实现的),而POST不会出现缓存(除非自己做特殊处理) 原因:get是通过`url冒号`传参的,而POST是设置请求主体(设置请求主体不会出现缓存,但是URL传递参数就会了) ``` //=>每隔一分钟重新请求服务器最新的数据,然后展示在页面中(页面中某些数据实时刷新) setTimeout(()=>{ $.ajax({ url:'getList?lx=news', ... success:function(result){ //=>第一次请求数据回来,间隔一分钟后,浏览器又发送一次请求,但是新发送的请求不管是地址还是传递的参数都和第一次一样,浏览器很有可能 会把上一次数据获取,而不是获取最新的数据 } }) },60000) ``` 解决方案 ``` //=>每隔一分钟重新请求服务器最新的数据,然后展示在页面中(页面中某些数据实时刷新) setTimeout(()=>{ $.ajax({ url:'getList?lx=news&__='+'Math.random()', //加上随机字符串 避免从缓存中获取数据 ... success:function(result){ }) },60000) ``` 4、GET请求没有POST请求安全(POST也并不是十分安全,只是相对安全) 原因:还是因为GET是URL传参给服务器,有一种比较简单的黑客技术:URL劫持,也就是可以把客户端传递给服务器的数据劫持掉,导致信息泄露 ### 关于URL 请求数据的地址(API地址),真实项目中,后台开发工程师会编写一个API文档,在API文档中汇总了获取哪些数据需要使用哪些地址,我们按照文档操作即可。 ![](https://box.kancloud.cn/f0b0abb7d3b3eea405c9b059e5b9fddb_1245x932.png) ### 关于ASYNC ``` ASYNC:异步(SYNC同步),设置当前AJAX请求是异步的还是同步的,不写默认是异步(TRUE),如果设置为false,则代表当前请求是同步的 ``` ### 用户名和密码 这两个参数一般不用,如果你请求的URL地址所在的服务器设定了访问权限,则需要我们提供可通行的用户名和密码才可以(一般服务器都是可以云溪匿名访问的) 开发时记录开发人员操作可能有用 ### xhr下的属性和方法方法 ``` xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) { var val = xhr.responseText; data = utils.jsonParse(val); } }; ``` #### AJAX状态码(xhr.readyState):描述当前AJAX操作的状态的 通过xhr的`__proto__`能找到 ![](https://box.kancloud.cn/5ec3fd06c6a400e89b5c8cb7659d3c68_430x221.png) >xhr.readyState > >0:UNSENT 未发送,只要创建一个AJAX对象,默认值就是0 >1:OPENED 我们已经执行了xhr.open这个操作 >2:HEADERS_RECEIVED 当前AJAX请求已经发送,并且已经接受到服务器端返回的响应头信息了 >3:LOADING 响应主体内容正在返回的路上 >4:DONE 响应主体内容已经返回到客户端 #### HTTP网络状态码(xhr.status) 记录了当前服务器返回信息的状态的状态 通过`xhr.status`获取 ![](https://box.kancloud.cn/56ce636cfd28a2cb5a31b6041c456664_1001x681.png) ![](https://box.kancloud.cn/fd035c7055d6cf0b36eb93bb56c4edae_795x576.png) 302 和 503(相对于302是没有服务器帮忙) #### 其它方法 ``` readyState:存储的是当前AJAX的状态码 ``` 如果请求的数据过大可能会触发多次`readyState=3`的`onreadystatechange`回调 ![](https://box.kancloud.cn/1e4e799bc448923c3b5080f911eca5a7_388x255.png) ``` response/responseText/responseXML:都是用来接收服务器返回的响应主体中的内容,只是根据服务器返回的格式不一样,我们使用拨通的属性来接收即可 responseText是最常用的,接收到的结果是字符串格式(一般服务器返回的数据都是JSON格式字符串) responseXML偶尔会用到,如果服务器端返回的是XML文档数据,我们需要使用这个属性接收 ``` ``` status:记录了服务器端返回的HTTP状态码 statusText:对返回状态码的描述 ``` ``` timeout:设置当前AJAX请求的超时时间,假设我们设置时间为3000(MS),从AJAX请求发送开始,3秒后响应主体内容还没有返回,浏览器会把当前AJAX请求任务强制断开 ``` ``` upload ``` onload 只是`readyState=4`时会触发 ![](https://box.kancloud.cn/a409664bfe19b38a1a6546b9b4d2767e_495x322.png) __proto__ 上的属性和方法 七个常用的 ``` abort():强制中断AJAX请求 getAllResponseHeaders():获取全部的响应头信息(获取的结果是一堆字符串文本) getResponseHeader(key):获取指定属性名的响应头信息,例如 xhr.getResponseHeader('date')获取响应头中存储的服务器时间 open():打开url地址 overrideMimeType():重写数据的MIME类型 send():发送AJAX请求(括号中书写的内容是客户端基于请求主体把信息传递给服务器) setRequestHeader(key,value):设置请求头信息(可以是设置的自定义请求头信息) ``` 事件 ![](https://box.kancloud.cn/6644986e45a93710a9f5c4abea02774d_306x296.png) ``` onabort:当AJAX被中断请求时触发 onreadystatechange:AJAX请求状态改变会触发这个事件 ontimeout:当AJAX请求超时,会触发这个事件 ``` ## 编码和解码 设置请求头的时候不能将内容设置成汉字 JS中常用的编码解码方法 ### 正常的编码解码(非加密) >escape/unescape:主要就是把中文汉字进行编码和解码 的(一般只有JS语言支持,一般经常用于前端页面通信的时候的中文汉字编码) ``` escape(str); //会将中文字符和空格进行编码,其他不变 unescape("...."); //解码 ``` >encodeURI/decodeURI:基本上所有的编程语言都支持 ``` encodeURI(str); decodeURI("..."); ``` >encodeURIComponent/decodeURIComponent 和第二种方式非常类似,区别在于 ![](https://box.kancloud.cn/f2a9652a2c0eecc005dcf4715b03ff8f_648x153.png) >需求:我们URL问号传递参数的时候,我们传递的参数值还是一个URL或则包含很多特殊的字符,此时为了不影响主要的URL,我们需要把传递的参数值进行编码。使用`encodeURI`不能编码一些特殊字符,所以只能使用`encodeURIComponent`处理 ``` let str = 'http://www.baidu.com/?' ,obj = { name:'ahhh' ,age:111 ,url:'http://www.ahhh.com/?lx=1' }; //=>把OBJ中的每一项属性名和属性值拼接到URL的末尾*(问号传参方式) for(let key in obj){ str += `${key}=${encodeURIComponent(obj[key])}&`; } //不能使用encodeURI必须使用encodeURIComponent,原因是encodeURI不能编码特殊的字符 console.log(str.replace(/&$/g,'')); ``` ![](https://box.kancloud.cn/749c5445c37097378766aea03209da3c_1270x606.png) ### 通过加密的方法进行编码解码 1、可逆转加密(一般都是团队自己玩的规则) 2、不可逆转加密(一般都是基于MD5加密完成的,可能会把MD5加密后的结果二次加密) ![](https://box.kancloud.cn/42823f1ad2966c2030e897efe0a67eba_724x287.png) ## AJAX的同步和异步 ### 同步 ![](https://box.kancloud.cn/12d5e48aef5c63d5dbf71f35061cc268_876x681.png) 当调用`xhr.send()`时,才真正开始ajax请求,状态才开始转变。 ``` AJAX这个任务:发送请求接收到响应体主体内容(完成一个完整的HTTP事务) xhr.send(); 任务开始 xhr.readyState === 4; 任务结束 ``` ``` let xhr = new XMLHttpRequest(); xhr.open("get","temp.json",false); xhr.onreadystatechange = ()=>{ console.log(xhr.readystate); }; xhr.send(); //->只输出一次 结果是4 ``` 同步的时候,以上代码结果为`只输出一次 结果是4` ``` let xhr = new XMLHttpRequest(); xhr.open("get","temp.json",false); xhr.send(); //->【同步】,开始发送AJAX请求,开启AJAX任务,在任务没有完成之前什么事情都做不了->LOADING->当readyState===4的时候AJAX任务完成,开始执行下面操作 xhr.onreadystatechange = ()=>{ console.log(xhr.readystate); }; //=>绑定方法之前状态已经为4了,此时AJAX的状态不会在改变成其它值了,所以时间永远不会被触发,一次都不执行 //SO,不要将send放在事件监听前 ``` ### 异步 ``` let xhr = new XMLHttpRequest(); xhr.open("get","temp.json"); xhr.onreadystatechange = function(){ console.log(xhr.readystate); } xhr.send(); ``` ![](https://box.kancloud.cn/c941e9f74b56bcc0d4184e5a7a72e597_886x643.png) ``` let xhr = new XMLHttpRequest(); xhr.open("get","temp.json"); xhr.onreadystatechange = function(){ console.log(xhr.readystate); } xhr.send(); ``` 会输出2、3、4 ``` let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ console.log(xhr.readystate); } xhr.open("get","temp.json"); xhr.send(); ``` 会输出1、2、3、4 AJAX特殊的一点,执行open状态变为1,会主动把之前监听的方法执行一次,然后再执行SEND ``` let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ console.log(xhr.readystate); } xhr.open("get","temp.json",false); xhr.send(); ``` 会输出1、4 ## jqueryのajax ## 封装原生ajax ``` 封装属于自己的AJAX类库 【支持的参数】 - url - method/type - data - dataType - cache - async - success ``` ``` ~function(){ class ajaxClass{ } window.ajax = function({url=null,method='GET',type=null,data=null,dataType='JSON',cache=true,async=true,success=null}={}){ let example = new ajaxClass(); example.url = url; example.method = type === null?method:type; ... }' }(); ``` ![](https://box.kancloud.cn/90582d7a150b03ba1c2259b2b8c4fedb_1500x728.png) 以上可以这么“优化” ![](https://box.kancloud.cn/3ace73769fcef32e6e92996828443c98_956x453.png) ajaxClass ![](https://box.kancloud.cn/3790760d9fa5ddca7d14526c7887589d_1333x757.png) 处理不同的mime 文件返回类型 ![](https://box.kancloud.cn/1eee8cb5e8d506f3be3920d2c267c8a0_700x443.png) ![](https://box.kancloud.cn/299e94736999cab57614cc1c06932ec6_1049x352.png) ![](https://box.kancloud.cn/52dd27fcaf0754efc943a3e842aee6b0_919x422.png) ## 关于post和get ![](https://box.kancloud.cn/3601ae8742456a02ccae94fa480fc38d_718x612.png) `send()`调用后会真正开始ajax请求,如果传了参这说明是post请求,发送的数据会放在请求报文里的`Request Payload`中 ![](https://box.kancloud.cn/c6c09e55432995247b10eaad8494e658_767x325.png) 注意光以上这样请求是会报错的, ![](https://box.kancloud.cn/3d754ac21544540e971798feeada685a_849x114.png) 当我们想给服务器传递数据数据时,还需要告诉服务器我们传送的数据是怎样的形式 ![](https://box.kancloud.cn/8f120ab63958c7941aa1ed96c263a04f_460x125.png) ## 关于上传和下载 ![](https://box.kancloud.cn/b9463ae99c108ad1b1ece104bf975377_490x397.png) ### 上传 ![](https://box.kancloud.cn/467259cb35c4287b5cf697a5697a6d00_381x129.png) html结构 ![](https://box.kancloud.cn/0d3910684c0ce0d41ec21f5babb0cf1e_743x216.png) ``` btn.onclick = function(){ var fileInput = document.getELementById("fileInput"); var xhr = new XMLHttpRequest(); var progress = document.getELementById("progress"); var progress_num = document.getElementById("progress_num"); //post方式上传 xhr.open("POST","地址",true); //上传文件进度监控 xhr.upload.onprogress = function(ev){ // console.log(ev); var t= ev.loaded/ev.total; var speed = Math.round(*100)+"%"; progress.style.width = t*500+'px'; progress_num.innerHTML = speed; } xhr.onload = function(){ console.log(xhr.status); console.log(xhr.responseText); } console.log(fileInput.value); //是文件路径,不是文件本身 console.dir(fileInput.files[0]); //上传文件上传的是文件的二进制 //文件转成二进制 var f = new FormData(); f.append('file',fileInput.files[0]); //key值 --- 文件 xhr.send(f); } ``` ![](https://box.kancloud.cn/310957763251504efb17a59b5b3770b6_334x272.png) ![](https://box.kancloud.cn/bd22bdca59e29a44d80e299502e7e9c0_663x259.png) 上传文件时必须转换为二进制的形式,否则会导致如下结果 ![](https://box.kancloud.cn/bc959d1750422da3ae3c09433e65959a_623x219.png) 当调用`FormData`进行转换后 ![](https://box.kancloud.cn/39accdc994a8c90790f9228f27ee2ce2_659x215.png) ### 如何监控上传进度 ![](https://box.kancloud.cn/24af1cdbb8e3f2d824e55b7b8ed43897_325x188.png) ![](https://box.kancloud.cn/85e062cae3479b27c08d098ce25f46cf_613x316.png) ### 上传限制 后端会有限制 ![](https://box.kancloud.cn/fb032813fa0df18b7194476f009ec808_507x109.png) post_max_size : 一次post总共允许传的大小(不仅包括上传文件大小还能包括其他杂七杂八的表单数据) upload_max_filesize : 上传文件的大小 ### 下载 ``` <a href="E:\xxx.zip">xxx.zip</a> ``` ![](https://box.kancloud.cn/a4aceb94a843c6e2eca64209ec50284a_362x120.png) 点击后浏览器会自动帮我们下载