AJAX
基本流程
1. 创建XHR对象
var xhr = new XMLHttpRequest();
2. 调用open方法
该方法接收3个参数:要发送请求的参数类型,请求的URL(相对路径,当然也可以用绝对路径)和是否异步操作的布尔值。
xhr.open("get", "example.txt", false);
open方法不会真正发送请求,而只是启动一个请求以备发送。
3. 调用send方法
该方法接收一个参数,即要作为请求主体发送的数据,如果不需要通过请求主体发送数据,则必须传入null(对某些浏览器是必须的)。调用send()后,前面的open请求才会被发送。
xhr.send(null);
以同步请求为例,js代码会等到服务器响应再执行操作,收到响应后,响应的数据会自动填充XHR对象的属性。
响应相关的XHR属性
- responseText: 保存从服务器接收的文本
- responseXML:保存从服务器接收的XML对象(即响应内容类型为"text/xml"或"application/xml")。
- status: 响应的HTTP状态
- statusText: HTTP状态的说明
无论内容类型是什么,响应内容主体都将保存在responseText属性中,而对于非XML数据,responseXML属性的值将为null。
接收响应后,首先检查status属性,状态码200意味着responseText属性的内容已就绪;404意味着url有效但资源本身不存在。
多数情况下,我们需要的是异步请求,才能让JavaScript继续执行而不必等待响应。此时可以通过检测readyState属性,该属性表示请求/响应过程的当前活动阶段,可取值如下:
- 0:未初始化。尚未调用open方法
- 1:启动,已经调用open但未调用send
- 2:发送,已经调用send但未接收到响应
- 3:接收:已经接收到部分响应数据
- 4:已经接收到全部响应数据
status扩展
status检测的是服务器的处理状态,状态码是由3个数字组成,分别表示不同状态类型(5种)
- 1XX 表示服务器正在处理中;
- 2XX 表示请求成功,比如200;
- 3XX 重定向,表示请求不成功,客户必须采取处理方式;
- 4XX 客户端错误,表示请求的地址或者文档不存在,比如404;
- 5XX 服务端或者后台出现问题,比如500,503……
4. setRequertHeader
设置http头部信息
xhr.open();
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send();
注意,setRequestHeader必须在open和send之间调用。
5. onreadystatechange
最好在调用open方法前指定onreadystatechange事件处理程序以保证浏览器兼容性。
只要readystate属性发生改变,都会触发一次readystatechange事件。通常我们只对readystate值为4的阶段来执行操作,因为此时数据已全部就绪。
xhr.onreadystatechange = function(){
if(xhr.readystate === 4 && xhr.status === 200){
...
}
}
如果网站存在多个AJAX请求,应该为XMLHttpRequest对象编写函数,每次的AJAX请求都通过函数来调用。
function loadDoc(url, cfunc){
xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.send(null);
}
function myFunction()
{
loadDoc("example.txt",function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
...
}
});
}
Get请求
Get是最常见的请求类型,最常用于向服务器查询某些信息。必要时,可以将查询字符串参数追加到URL的末尾。对XHR而言,位于open方法的URL末尾的查询字符串( 经过正确的编码 )必须按如下形式:
xhr.open("get", "example.php?name1=value1&name2=value2",true);
所有的名-值对都必须用"&"分隔。
下面这个函数可以辅助向现有URL的末尾添加查询字符串参数:
function addURLParam(url, name, value){
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeponent(value);
return url;
}
Post请求
通常用于向服务器发送应该被保存的数据,与get请求不同,post请求需要将数据主体通过send方法发送
xhr.open("post", "example.php", true);
xhr.send(string);
默认情况下,服务器对post请求和提交web表单不会一视同仁,所以如果需要像 HTML 表单那样 POST 数据,请使用 setRequestHeader() 来添加 HTTP 头。然后在 send() 方法中规定您希望发送的数据:
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("fname=Henry&lname=Ford");
GET 还是 POST?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
- 无法使用缓存文件(更新服务器上的文件或数据库)
- 向服务器发送大量数据(POST 没有数据量限制)
- 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
Tips: 在表单提交的情况下,使用get提交后可直接在url后缀看到用户输入的表单信息而post不可见
FormData
可以将表单数据序列化
var data = new FormData();
data.append("name", "value");
上面代码创建了一个FormData对象,并向其中添加了一些数据。
也可以表单元素的数据预先向FormData对象填入键值对。
var data = new FormDate(document.forms[0]);
创建FormData的实例后,可以直接将其传给XHR的send方法
var form = document.forms[0];
xhr.send(new FormData(form));
使用FormData还有一个便利之处就是不必为XHR对象设置请求头部
timeout超时设定
表示请求在等待响应多少毫秒之后就终止。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function() {
try {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText);
}
else{
alert("Request was unsuccessful: " + xhr.status);
}
}
catch (ex) {
console.log("Timeout!!");
}
};
xhr.open("GET", "http://www.baidu.com");
xhr.setRequestHeader("Content-type","Access-Control-Allow-Origin");
xhr.timeout = 1000;
xhr.ontimeout = function(e) {
console.error("Timeout!!")
};
xhr.send(null);
上面使用try-catch是为避免在超时终止时去访问status导致报告出错。
关于跨域请求
jsonp
jsonp由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定。而数据就是传入回调函数中的JSON数据。形式如下:
<script>
var localHandle = function(data){
alert("返回的数据:" + data);
}
</script>
<script src="http://remoteserver.com/remote.js?name=value&callback=localHandle"></script>
remote.js代码如下:
localHandler({"result":"我是远程js带来的数据"});
服务器根据请求里的callback=localHandle知道了本地的回调函数名称localHandle,查询字符串为name=value,然后服务器执行相对应的函数并返回数据给本地的localHandle回调函数。
建议动态的创建script来查询
<script>
var localHandle = function(data){
alert("返回的数据:" + data);
}
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http://remoteserver.com/remote.js?name=value&callback=localHandle";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
jsonp优点
简单易用,能够直接访问响应文本,支持浏览器和服务器之间的双向通信
jsonp缺点
- 安全性不足。借助JSONP有可能进行跨站请求伪造(CSRF)攻击,当一个恶意网站使用访问者的浏览器向服务器发送请求并进行数据变更时,被称为CSRF攻击。由于请求会携带cookie信息,服务器会认为是用户自己想要提交表单或者发送请求,而得到用户的一些隐私数据。
- 错误原因不易找。JSONP缺乏错误处理机制,如果脚本注入成功后,就会调用回调函数,但是注入失败后,没有任何提示。这就意味着,当JSONP遇到404、505或者其他服务器错误时,你是无法检测出错原因的。我们能够做的也只有超时,没有收到响应,便认为请求失败,执行对应的错误回调。
- 只能适用于get请求。只能使用GET请求就意味着很多限制,提交到服务器的数据量将受限于浏览器的最大URL长度。
jsonp封装
function jsonp(url, jsonpCallback, success){
let script = document.createElement("script");
script.src = url;
script.async = true;
script.type = "text/javascript";
window[jsonpCallback] = function(data){
success & success(data);
};
document.body.appendChild(scirpt);
jsonp(
"http://xxx",
"callback",
function(value) {
console.log(value);
}
);
}
CORS
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。详情后续补充...
AJAX应用场景
第一、请求的提交是为了页面数据的显示,这时候用户一般不希望看到页面的刷新,是使用AJAX的一个最佳时候。
第二、如果请求提交后,用户能从页面感觉到提交结果,这时候,也最好不要有页面刷新,推荐使用AJAX技术。
第三、如果请求提交后,用户不能从页面感觉到提交动作,如绝大多数时候的数据的增加和修改,这时候则需要页面刷新,不能使用AJAX技术。
第四、复杂的UI,以前对于复杂的C/S模式的UI,B/S模式一向采取逃避的方法,现在则可以放心大胆的使用AJAX来加以解决。
常见问题解决:
1、 第一、输入值校验的问题 申请用户的时候检查用户名是否重复,用AJAX访问后台,既不需要刷新页面,也没有过多的JS代码
2、第二、级联显示的问题 访问后台吧,页面需要刷新;JS代码量大,影响内存,数据不安全;所以常级联选择框,级联菜单,导航树等
3、第三、请求结果只改变部分页面 如,论坛的回复帖子和帖子列表在一个页面上的时候。这两个UI在一个页面上,用户体验比回复帖子在另外一个页面好。但回复后要对整个页面进行刷新,这种感觉就不好了。你看,那么大一个帖子列表,只增加你的一个回复,却要对整个页面进行刷新,不管从哪个角度来看都不好。
4、第四、由于技术原因而使用iframe的问题 避免iframe的嵌套引入的技术难题
5、第五、数据录入和列表显示在同一个页面 C/S 模式的UI中常常有数据录入和数据列表显示在同一个界面上,这样对于用户来说有很好的用户体验,用户录入的结果马上就能在同一界面显示。但是在B/S的 UI上,由于需要提交刷新的问题,我们经常把数据的录入和数据显示分别放在两个不同的页面上。很显然,这样的用户体验肯定没有C/S模式来得好。像这样的 问题还有很多,在B/S模式下,都因为技术的原因而选择其他的解决办法。现在我们可以自豪的使用AJAX来宣告可以做出和C/S模式一样复杂的UI了
6、第六、翻页问题 不需要刷新的翻页