在使用 jQuery 开发了不少网站以后,对原生的 JS 是越来与生疏了,可是对于任何一门语言来讲,框架只会让事情事半功倍,但归根结底,仍是语言原生的特性是根本。
对于JS来讲,基于浏览器这个宿主环境,其有不少尴尬的地方,例如兼容性很差,对于多种浏览器来实现兼容,致使代码很臃肿;原生的不少方法属性使用十分不便捷等。但对于一门语言要想深刻,掌握其原生的特性仍是十分必要的,毕竟相对于 jQuery 这种类库或者框架来讲,要知其然,还要知其因此然。
来自维基百科的关于AJAX 的解释是:Asynchronous JavaScript and XML(异步的JavaScript和XML技术,但其实AJAX的实现不必定和 XML 有关系,也即AJAX和数据格式无关),是一种建立交互式网页应用的网页开发技术,是多种技术的组合。目的就是让前台的数据交互变得更快捷,不用刷新页面就能够完成数据的更新。
AJAX 的优势很明显,利于用户体验,在不刷新页面的状况下利用DOM交互能够更新页面内容,不会打断用户的操做,因为其获取和发送的数据量小的特色(获取数据格式可使XML或者JSON或者其余,不是整个页面),减少服务器压力也是它的一个优势。
而缺点,就是倍受人们关注的SEO问题(Google使用#!这种规定来支持AJAX的SEO,可是百度貌似尚未支持,因此作互联网的人,若是用百度的话,我劝你趁早转行,不要告诉我不稳定,不会那啥),前进后退问题(能够用其余方法解决HMTL5新增了相应的API),以及必须重视的安全问题等都是它现存的一些缺点。
而AJAX 的应用范围,这其实这是一个太宽泛的问题,各人及各项目都有不一样的用处,只要是可以提高用户体验的地方,均可以使用AJAX,可是对于什么是用户体验,又是一个写几本书都写不完的东西了。举例子来讲,对于Gmail这类应用,其实能够看作SPA(单页面应用, Single Page Application)应用,因此整站都不须要刷新,而对于G+ 这种网站来讲,虽然能够不整站刷新,可是整站刷新其实并无什么不妥的地方,而不整站刷新,反而带来了页面假死的现象。听说知乎内部也有相似于G+的项目,可是我以为这种应用,对于知乎来讲,其实并不必定适合,在愈来愈快的网速这种状况下,其实仍是整个页面刷新来的舒服,固然了,这个只是我的观点。而对于小面积更新数据来讲,好比对用户名或者邮箱惟一性(输入框失去焦点后)等数据判断、选项卡(选项卡切换AJAX加载)、弹出的登陆注册窗口(能够AJAX返回HTML结构)以及用户提交表单信息后的反馈信息等等,这种形式确实很是合适的。
来了这么多开胃菜,其实不少人已经烦了,那么咱们就开始来点干货,下面将经过一个新用户注册的过程来浅尝辄止的讲解下AJAX的基础知识。
注册表单要求:用户名为邮箱,惟一性,密码必须大于六位,为了简单起见,这里不作过多合法性验证。
AJAX出现以前,若是要注册新用户的话,用户必须填写完全部必填信息,而后单击提交按钮,将数据提交到服务器以后,才能验证数据的合法性,例如验证邮箱惟一性,提交表单以后,必须通过服务器判断邮箱的惟一性,而后刷新页面在显示此邮箱是不是惟一的,可用来注册的(其实还能够用隐藏iframe来实现)。而有了AJAX以后,当用户将邮箱输入完成,此输入框失去焦点的时候,经过AJAX查询服务器,而后给出邮箱是不是惟一的结果,虽然也通过了服务器的交互,可是页面没有刷新,而且交互的数据量很是小,发送的数据除了必要的头信息就是查询数据,而接受的数据除了头信息以外,甚至能够减小到只用数字0或者1来表示数据是否合法。
来看HTML结构代码:php
<form id="register" method="POST" action="register.php"> <label>邮 箱:<input type="text" id="email" placeholder="请输入邮箱地址"></label> <label>密 码:<input type="password" id="password" placeholder="请输入密码"></label> <input type="submit" value="提交"> </form>
除了表单页面以外,还须要有一个后端页面register.php来负责验证数据的合法性以及注册过程,这里是后端页面,就不给出具体代码了。
接下来就交给AJAX来实现了,当邮箱输入框失去焦点时使用 GET 方式来和后台交互以验证此邮箱的惟一性。
而当点击提交按钮时,使用 POST 方式来和后台交互,以验证是否可以注册成功。
除了HTML和PHP以外,接下来就是AJAX发挥做用的时候了。
提到AJAX,就不得不提XMLHttpRequest(XHR) 对象,它是W3C的一个标准,也是AJAX最核心也是最底层的对象,可是悲剧的是IE系列,直到IE7+ 才实现了W3C的标准XHR对象,可是咱们仍是要支持IE6的啊(万恶的IE6,这里不得不说,其实不少地方都误解了微软,在IE5中微软就实现了XHR对象,后来各个浏览器厂商才逐渐的实现此对象,只不过IE5/IE6是按微软本身的方式来实现的,使用的是ActiveX对象),因此不得不作兼容性处理。
为了兼容IE6,那咱们就写兼容性的代码吧。html
function creatXHR(){ if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else if(window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP"); } else { alert(‘此浏览器不支持 XHR !’); } }
使用此代码来建立一个建立兼容XHR对象的函数,第二行先判断window是否有XMLHttpRequest这个属性,若是有则证实是支持W3C标准的XHR对象,不然就是IE6了(已经能够不用考虑其余浏览器了),使用微软的方式来建立XHR对象,所幸的是其余浏览器的实现和IE早期的实如今内部是兼容的,W3C也兼容(其实W3C的不少标准都是采纳于各个浏览器已经实现的事实标准)。
咱们先来想一想数据验证的过程,获取数据,建立XHR对象,而后使用XHR对象的方法来与服务器交互。先无论具体的交互过程,无论是数据验证仍是注册,都是这么三步,那么接下来就实现另外的函数。
发送数据并验证:前端
function validateData(obj,method,url,data){ var xhr = creatXHR(), iscancel = setTimeout(function(){xhr.abort();console.log("超时取消!");},5000); // 设定超时取消 xhr.onreadystatechange = function() { //绑定事件 if (xhr.readyState == 4 && xhr.status ==200){ //此时数据可用 var res = (window.JSON)?(JSON.parse(xhr.responseText)):(eval("("+xhr.responseText+")"));//解析 json字符串 obj.nextSibling.innerHTML = res.msg; if (res.code == 0 || res.code == 2) { obj.nextSibling.style.color = 'red'; } else { obj.nextSibling.style.color = '#5B636A'; }; clearTimeout(iscancel);//若是响应成功,取消定时函数 } } //检测 Method 方法,GET POST if (method.toLowerCase() === "get") { xhr.open("GET",modiUrl(url,data),true); xhr.send(null); } else if (method.toLowerCase() === "post"){ xhr.open("POST",url,true); xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(serialize(data)); }; }
而后来详细阐述一下这个xhr对象的相关内容(获取输入数据就是很正常的获取数据的过程,这里很少说了)。
对于vliData() 函数中的XHR而言(并不仅是此函数中,全部的XHR对象都同样,后面相同,此处只是为了好说明),要使用XHR对象时,必须调用的第一个方法是open(),即:xhr.open();此方法须要接收三个参数:
第一个参数是要发送的请求的类型(GET POST等),
第二个参数即请求的 URL,GET请求能够将查询参数追加到URL的末尾,可是每一个参数的名值对必须通过encodeURIComponent() 编码才可以使用,否则容易发送错误(虽然通常状况下参数的名称都为拼音或者英文,可是仍是应该养成每次编码的好习惯)。
第三个参数即表示是不是异步发送请求,布尔值,true或者false。上例中已有使用,即:web
xhr.open("GET","register.php?"+encodeURIComponent('email')+"="+ encodeURIComponent(email),true);
这里使用了 get 方法,异步获取register.php 这个url的数据(url必须同域同端口同协议,不然报安全错误,能够绝对路径或者相对路径)。可是当调用open() 方法的时候并不会真正的发送请求,而是启动一个请求以备发送。而要发送请求,就必须调用send()方法才行。例如上面的代码所示,即:ajax
xhr.send(null);
这里的send()必须接受一个参数,即:要做为请求主体发送的数据。若是不须要经过请求主体发送数据,则必须传入 null ,由于不传入的话,有些浏览器会报错。
好了,数据发送完毕了。
数据发送完毕,等到服务器处理完毕并返回,浏览器收到响应的时候,相应的数据就会自动填充XHR对象的相应属性。XHR对象的相应属性主要包括如下几种。
responseText : 做为响应主体被返回的文本。
responseXML : 若是响应的内容类型是text/xml 或者 application/xml 的时候,这个属性保存这响应数据的XML DOC 文档。
status : 响应的http状态。
statusText : http状态的说明
在接收到响应以后,第一步就是要检查 status 属性,以肯定响应已经成功返回,通常来讲,能够将http的状态码为200时做为成功的标志(其实还有304等),此时responseText 的内容已经就绪,若是内容类型正确的话,responseXML 也已经能够访问。
可是咱们这里使用的是异步请求,不像同步请求那样会将JS执行阻塞,当收到响应后才会执行后面的代码。所以要经过事件来肯定XHR的状态,由于XHR对象的open() 或者 send() 方法被调用以后,并不清楚XHR 到达什么状态了,因此还要判断 XHR 的状态决定以后该如何处理。
对于 XHR 状态的判断,能够检测 XHR 对象的 readyState 属性,该属性表示 XHR 对象请求/响应过程的当前活动阶段,可取值以下:
0 : 未初始化,即还没有调用 open() 方法,
1 : 启动,已经调用open() 方法,但还没有调用 send() 方法,
2 : 发送,已经调用send() 方法,但还没有接收到响应,
3 : 接收,已经接收到部分响应数据,
4 : 完成,已经接收到所有响应数据,便可以在客户端使用数据了。
同时,XHR 对象还有一个事件readystatechange ,而readyState 属性的值每变化一次,都会触发一次readystatechange 事件,所以能够经过检测此事件每次触发时readyState属性的值,通常状况下,咱们只对readyState的值为 4 时的状况感兴趣,由于此时全部的数据已经就绪可用,不过为了确保浏览器的兼容性,必须在调用 open() 以前指定readystatechange事件处理程序。如上代码所示。json
xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status ==200){ alert(xhr.responseText); } }
在onreadystatechange事件每次触发时,检测readyState的值,若为 4 ,且status 为200(http状态,前面已说明)时才可使用此数据。
另外,在接收到响应以前,还可使用 abort() 来取消异步请求。例如当请求相应时间超长时,能够调用此方法,而后将其异步请求取消。如:后端
var iscancel = setTimeout(function(){xhr.abort();},5000); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status ==200){ alert(xhr.responseText); clearTimeout(iscancel); } }
便可在超过5秒钟未收到响应,因为超时而取消本次请求。
若是请求发送成功,而且也接收成功的话,responseXML或者responseText也就可用了,此时能够按返回不一样的数据来进行处理。目前为了开发简便起见,通常返回的就是html结构或者json数据,关于数据的处理和使用,这里就不涉及了。
对于 HTTP 来讲,每一个请求和响应都会包含相应的头部信息,有的有用,有的没用,同时 XHR 也提供了相应的方法来操做这两种头部的信息,由于其用处不是很是大,因此这里就再也不展开了(不过有时候能够经过自定义的头部信息加上后端的配合来实现一些额外的功能),只是在使用 POST 方式请求的状况下,须要用到setRequestHeader() 方法。
以上就是一个完整的利用原生的 JS 来写的GET 方式下的 AJAX请求,而POST 方式下的请求咱们尚未接触到。那么咱们下面就开始阐述一下POST方式下原生 JS 的AJAX请求的实现方法。
前面已经说过,XHR对象调用的第一个方法是open(),open() 方法的第一个参数是请求类型,因此POST 方式的 open() 方法即为:浏览器
xhr.open("POST","register.php,true);
而第二步就是将发送的数据经过 send() 方法发送出去(前面已经讲过)。可是服务器端对于提交的web表单必须通过处理,由于对于POST提交的非表单数据,其处理方式是不一样的,也就是后端程序必需要读取前端发过来的原始表单数据,并从中解析出有用的部分,所以咱们必须经过XHR模拟表单数据的提交,这也就用到了自定义头部信息。为了保证兼容性,使自定义头部发送成功,必须在open() 方法以后,在 send() 方法以前,设定自定义头部(若是不设定自定义头部的话后端是否能够获取提交的数据呢,答案是能够,可是就不能使用$_POST这种超级全局变量,而只能经过$HTTP_RAW_POST_DATA来获取了,此处基于PHP而言)。安全
xhr.open("POST","register.php,true); xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(data);
自定义头部的意思就是表单提交时的内容类型。
而后就是要发送的数据了,由于最初XHR设计主要是为了处理 XML,因此这里能够传入XML DOM文档,传入的文档在通过序列化以后做为请求主体被提交到服务器,固然了,这里也能够传入任何想发送到服务器的字符串。咱们这里使用提交字符串方式。
因为 POST 数据的格式与查询时(GET)字符串格式相同,因此只须要将表单中的数据进行序列化,而后经过XHR发送到服务器便可。服务器
xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status ==200){ alert(xhr.responseText); } } xhr.open("POST","register.php,true); xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(data);
而后就是对表单数据的序列化了。其实和GET数据格式相同,即email=a@b.com&site=yimity.com,可是要编码。这里就很少作讨论了。
最终的代码为:
var data = "email=yimity%40gmail.com&password=123456"; xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status ==200){ alert(xhr.responseText); } } xhr.open("POST","register.php,true); xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(data);
经过一个例子,将AJAX最基本的 GET 和POST 讲清楚了,其实AJAX还能够用来上传文件,不过这都须要支持HTML5的浏览器才可实现(隐藏的iframe也能够实现,但不属于纯AJAX了),HTML5 中的 XMLHttpRequest 2 级实现了不少XMLHttpRequest 1级中的细节,而且实现了不少很是有用的API,例如文件上传, FormData 等,可是这些不属于基础的AJAX交互范畴,因此这里就不涉及了。
打完收工,其实代码这东西,仍是例子最重要,不过个人代码很丑。点此查看。