Ajax 、comet,红宝书

红宝书是入门必备,但是里面的有些api已经被废弃,如果运行的话会报错,我查了文档才知道有些是被废弃的, 开始只是看,动手敲才能踩坑,这篇博文弄了一天,期间碰到各种问题

友情提示

如果要练习ajax的,最好下一个wamp,可以在本地搭建一个服务器,非常好操作,

创建XHR对象

下面这个创建XMLHTTPRequest对象的createXHR()函数兼容了IE7以下的版本,如果不需要兼容的话只需要

new XMLHttpRequest()
function createXHR() {
     
        alert();
        if(typeof arguments.callee.activeXString != "string") {
            var versions = [
                "MSXML2.XMLHttp.6.0",
                "MSXML2.XMLHttp.3.0",
                "MSXML2.XMLHttp"
            ],
            i,
            len;
            for(i=0,len = versions.length;itry{
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                }catch(ex) {

                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString)
    }

创建好了,我们怎么使用呢,下面来说一下

使用XHR对象

同步

首先要调用的是open方法

xhr.open("get", "data.json", false);

看上面的代码很容易知道open有三个参数,

 1. 要发送的请求类型,get、post等
 2. 请求的URL,即你要请求的文件的地址相对于当前页面,绝对路径也可以
 3. 是个布尔值,表示是否异步请求

需要注意的是调用open方法并不会真正发送请求,只是启动一个请求以备发送,
只能向同一个域中使用相同端口和协议的URL发送请求,若URL与启动请求的页面有任何差别,都会引发安全措施
send方法接受一个参数,作为请求主题发送的数据,如果不需要,最好传入null,因为这个参数对有的浏览器来说是必需的,调用send后,请求就会被分派到服务器
建议通过status来决定下一步操作,因为statusText在跨浏览器使用的时候不可靠,如果响应主体非XML,则responseXML属性值为null
下面是一个简单的同步请求

var xhr = createXHR();
xhr.open("get", "data.json", false);
    xhr.send(null);
    if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
        alert(xhr.statusText);
        alert(xhr.responseText);
    } else {
        alert("fail" + xhr.status);
    }

异步请求

检测readyState属性的值,查看响应过程当前活动阶段,属性可取值:

  • 0,未初始化,还没调用open。
  • 1,启动,已调用open,未调用send
  • 2,发送,已调用send,但尚未接收到响应
  • 3,接收,已接受到部分响应数据
  • 4,完成,已经接收到全部响应数据,可在客户端使用

只要readyState属性值改变就会调用readystatechange事件,不过必须在调用open之前指定,确保跨浏览器的兼容

xhr.onreadystatechange = function() {
        console.log(this);
        if(xhr.readyState == 4){
            if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                console.log(xhr.statusText);
                console.log(xhr.responseText);
            } else {
                console.log("fail" + xhr.status);
            }
        }
        console.log("改变");
    }
    xhr.open("get", "data.json", true);
    xhr.send(null);

运行结果截图
Ajax 、comet,红宝书_第1张图片
上面的代码中,不要使用this,用实例对象xhr,因为回调的时候,作用域就会改变
如果使用this对象,在有的浏览器中会导致函数执行失败,或者导致异常.因此使用实际的xhr对象实例变量比较靠谱.这里也没搜到具体的解释,只是说调用这个可能会因为作用于问题而失败

HTTP头部信息

这里的头部信息有两种,

  • 响应头信息:是服务器返回的信息,可以获取他但是不可以设置他。
  • 请求头信息:是客户端向服务器发送的信息,可设置但是不可以获取他,例如不能用下面介绍的getRequestHeader()获取他。

下面头信息一般会随XHR请求发送

  • Accept: 浏览器能够处理的内容类型。
  • Accept-Charset: 浏览器能显示的字符集。
  • Accept-Encoding: 浏览器能够处理的压缩编码。
  • Accept-Encoding: 浏览器能够处理的压缩编码。
  • Accept-Language: 浏览器当前设置的语言。
  • Cookie: 当前页面设置的任何Cookie.
  • Referer: 发出请求的页面的URI。 HTTP规范装饰这个头部字段拼写错了,为保证与规范一致,只能将错就错(原拼法:referrer)
  • User-Agent: 浏览器的用户代理字符串。
    setRuquestHeader()设置自定义的请求头部信息,open之后且send之前调用,参数:
    1.头部字段名称
    2.字段值
xhr.onreadystatechange = function() {
        if(xhr.readyState == 4){
            if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                console.log(xhr.statusText);
                alert(xhr.responseText);
            } else {
                alert("fail" + xhr.status);
            }
        }
    }
    xhr.open("get", "data.json", true);
    xhr.setRequestHeader("myheader", "myvalue");
    xhr.send(null);

Ajax 、comet,红宝书_第2张图片
建议:字段名称自己想名字,不要使用浏览器定义的,有的浏览器允许我们重写默认的头部信息有的不允许,所以这样有可能影响服务器响应。

  1. getResponseHeader()
  2. getAllResponseHeader()
xhr.onreadystatechange = function() {
        if(xhr.readyState == 4){
            if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                console.log(xhr.statusText);
                console.log(xhr.responseText);
                console.log(xhr.getAllResponseHeaders());
            } else {
                alert("fail" + xhr.status);
            }
        }

    }
    xhr.open("get", "data.json", false);
    xhr.setRequestHeader("myheader", "myvalue");

    xhr.send(null);

GET请求

常用于向服务器查询某些请求,可以将查询字符串追加到URL末尾,对xhr来说,追加的字符串的每个参数必须经过编码,所有名值对用&隔开

xhr.open("open", "example.php?name1=value1&name2=value2", true);

创建一个函数向传入的URL末尾追加经过编码的字符串

function addURLParam(url,name,value) {
     
        if(url.indexof("?") == -1){
            url += '?';
        } else{
            url+= '&';
        }
        url+=encodeURIComponent(name) + '=' + encodeURIComponent(value);
        return url;
}

小例子:

    document.addEventListener('click',function() {
     
        var xhr = new createXHR();
        var url = 'demo.php?rand='+Math.random();
        url=addURLParam(url,'name','Le&e');
        url=addURLParam(url,'age',100);
        alert('url:   ' + url);
        xhr.onreadystatechange = function() {
     
        if(xhr.readyState == 4){
            try{
                    if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                        console.log(xhr.statusText);
                        alert('responseText:    ' + xhr.responseText);
                        console.log(xhr.getAllResponseHeaders());
                    } else {
                        alert("fail" + xhr.status);
                    }
            } catch(e) {

            }
        }
    };
    xhr.open("get", url,true);
    xhr.send(null);
    });
    function addURLParam(url,name,value) {
     
        if(url.indexOf("?") == -1){
            url += '?';
        } else{
            url+= '&';
        }
        url+=encodeURIComponent(name) + '=' + encodeURIComponent(value);
        return url;
    }

我的php文件,中文会有乱码问题,所以要设置utf-8


header('Content-Type:text/html;charset=utf-8');
// echo Date('Y-M-D H:i:s');
print_r($_GET);
// print_r($_POST);
if($_GET['name']=='Le&e'){
    echo '噜瑞';
}
?>

Ajax 、comet,红宝书_第3张图片
Ajax 、comet,红宝书_第4张图片

POST请求

用于向服务器发送应该被保存的数据,把数据作为请求的主体提交,它请求的主体可以包含非常多的数据,且格式不限。。

document.addEventListener('click',function() {
     
        var xhr = new createXHR();
        var url = 'demo.php?rand='+Math.random();
        alert('url:   ' + url);
        xhr.onreadystatechange = function() {
     
        if(xhr.readyState == 4){
            try{
                    if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                        console.log(xhr.statusText);
                        alert('responseText:    ' + xhr.responseText);
                        console.log(xhr.getAllResponseHeaders());
                    } else {
                        alert("fail" + xhr.status);
                    }
            } catch(e) {

            }
        }
    };
    xhr.open("post", url,true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');  // 模拟表单提交
    xhr.send('name=Lee&age=100');
    });
    function addURLParam(url,name,value) {
     
        if(url.indexOf("?") == -1){
            url += '?';
        } else{
            url+= '&';
        }
        url+=encodeURIComponent(name) + '=' + encodeURIComponent(value);
        return url;
    }

php文件


header('Content-Type:text/html;charset=utf-8');
print_r($_GET);
print_r($_POST);
if($_POST['name']=='Lee'){
    echo '噜瑞阿';
}
?>

Ajax 、comet,红宝书_第5张图片
Ajax 、comet,红宝书_第6张图片

FromData

为表单数据的序列化,以及创建与表单格式相同的数据提供便利。

  1. 创建
var data= new FormData();
data.append("name", "lurui");

append方法接受两个参数,键、值,分别对应表单字段的名字和字段中包含的值,可以添加任意多个键值对,通过向FormData构造函数中传入表单元素,也可以用表单元素的数据预先向其中填入键值对:

var data= new FormData(document.forms[0]);
data.append()
var xhr = createXHR();
    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4){
            if((xhr.status >= 200 && xhr.status<300) || xhr.status == 304){
                console.log(xhr.responseText);
            } else {
                alert("fail" + xhr.status);
            }
        }
    };
    xhr.open("post", "data.json", true);
    var form = document.getElementById("user-form");
    xhr.send(new FormData(form));

超时设定

IE8、timeout属性,表示请求在多长时间后没接收到响应就执行ontimeout事件

进度事件ProgressEvents

load

用于替代onreadystatechange(),响应接收完毕触发load,因为没必要检查readystate属性,load接收一个event对象,其target属性指向XHR对象实例,按理来说通过target就可以访问XHR对象的所有属性和方法了,然而并非所有浏览器都实现event对象,所以不能用,只要浏览器接受到服务器响应不管状态如何都会触发load,而这意味着你还是要检测status属性,才可以确定数据是否可用。

progress事件

open方法之前指定。

xhr.onprogress = function(event) {
        console.log(event.position);  
        var divs = document.getElementById("a");
        if(event.lengthComputable){
            divs.innerHTML = "received " + event.position + ' of ' + event.totalSize + ' bytes';
        }
    }

Ajax 、comet,红宝书_第7张图片
FF、chrome显示undefined,上面的截图是360的运行结果,其实他们都不支持totalSise,要换成total,position要换成loaded,但是如果不换IE也会有结果不报错,但是FF、chrome会报undefined,不显示正确结果

跨资源共享

通过XHR实现ajax通信的一个主要限制是跨域安全策略,默认情况下,XHR对象只能访问与包含他的页面位于同一个域中的资源,可以防止恶意行为。
CORS跨资源共享

IE对CORS的实现

先看这个图,检查了好几遍代码无误后我去百度,得到的结果
Ajax 、comet,红宝书_第8张图片
IE8引用了XDR(XDomainRequest)类型,此对象与XHR类似,但可以实现安全可靠的跨域通信,
XHR与XDR的不同之处

  1. cookie不会随请求发送,也不会随响应返回
  2. 只能设置请求头部信息中的Content-Type
  3. 不能访问响应头部信息
  4. 只支持GET、POST
    XDR的open只接受两个参数,请求类型和url
    所有XDR请求都是异步的。不能创建同步,请求返回时调用load,响应数据在responseText中,

其他浏览器对CORS的实现

看一下浏览器对xhr的支持
PC端:
Ajax 、comet,红宝书_第9张图片
移动端:
Ajax 、comet,红宝书_第10张图片
XHR跨域请求时,url写成绝对地址即可,通过跨域XHR对象也可以访问status,statusText属性,也支持同步请求,跨域XHR有一些必须的限制:
1. 不能使用setRequestHeader自定义头部
2. 不能发送和接收cookie
3. 调用getAllResponseHeaders方法总会返回空字符串
由于无论是同源还是跨源都是用相同的接口,因此对于本地资源最好用相对url,对于跨源用绝对url,这样可以消除歧义,避免出现限制访问头部或本地cookie信息问题。

Preflighted Request

透明服务器验证机制,支持使用我们的自定义头部、get或post以外的方法、以及不同类型的主体内容

跨浏览器的CORS

function createCORSRequest(method,url) {
     
        var xhr = new XMLHttpRequest();
        if("withCredentials" in xhr){
            xhr.open(method,url,true);
        } else if(typeof XDomainRequest != 'undefined') {
            xhr = new XDomainRequest();
            xhr.open(method,url);
        } else{
            xhr =null;
        }
        return xhr;
    }

JSONP

function handleResponse(response) {
        console.log(response);
        alert("ip address" + response.ip + " in " + response.city + "," + response.region_name);
    }
    var script = document.createElement("script");
    script.src = "http://freegeoip.net/json/?callback=handleResponse";
    document.body.insertBefore(script, document.body.firstChild);

Ajax 、comet,红宝书_第11张图片
Ajax 、comet,红宝书_第12张图片

Comet

更高级的ajax技术。ajax是一种页面向服务器请求数据的技术,Comet则是服务器向页面推送数据的技术,让信息近乎实时的推送到页面上,适合体育比赛分数、股票报价等。
短轮询:即浏览器定时向服务器发送请求,检测有没有更新的数据
实现Comet的方法

  1. 长轮询,是传统轮询(短轮询)的一个翻版,页面发送一个请求,然后服务器一直保持连接打开,直到有数据可以发送。发送完数据后浏览器关闭连接,随即又发起一个到服务器的请求,这一过程在页面打开期间一直持续不断
  2. 流,在页面的整个生命周期内只是用一个http连接,即浏览器发送一个请求,服务器保持连接打开,周期性的向浏览器发送数据。

实现HTTP流原理:通过侦听readystatechange(如果支持)事件及检测readyState值是否为3,利用xhr对象实现。随着不断从服务器接收数据,readyState值会周期性的变为3,当为3时,responseText中就会保存所有接受到的数据,此时较之前接受到的数据决定从什么位置开始去的最新的数据,
实现代码如下:

function createStreamingClient(url,progress,finished) {
     
            var xhr = new createXHR(),
                received = 0;
            xhr.open("get",url,true);
            xhr.onreadystatechange =function() {
     
                var result;
                if(xhr.readyState == 3) {
                    result = xhr.responseText.substring(received);
                    received += result.length;
                    progress(result);
                } else if(xhr.readyState ==4) {
                    finished(xhr.responseText);
                }
            };
            xhr.send(null);
            return xhr;
        }
        // 实例
        var client = createStreamingClient("data.json", function(data) {
     
            console.log("received : " + data)
        }, function(data) {
     
            alert("done");
        })

Web Sockets

贴一段书上的原话
Ajax 、comet,红宝书_第13张图片
MDN上的websockets的浏览器支持情况
Ajax 、comet,红宝书_第14张图片
实例化

var socket = new WebSocket("ws://www.example.com/server.php");
//关闭时用
socket.close();

注意: 构造函数必须传入绝对URL,同源策略对websockets不适用,因此它可以打开任何站点的连接,是否能与某个域中页面通信完全取决与服务器
表示当前状态的readyState(类比XHR,不过两者的属性值不同),他永远从0开始。调用close后,值变为2(正在关闭),关闭后变为3. 属性值:

  1. WebSocket.OPENING(0):正在建立连接
  2. WebSocket.OPEN(1):已建立连接
  3. WebSocket.CLOSING(2):正在关闭连接
  4. WebSocket.CLOSE(3):已关闭连接

websockets没有readystatechange事件

websockets只能通过连接发送纯文本数据,所以对于复杂的数据,在发送前要进行序列化(JSON.Stringify())
当服务端向客户端发来消息时,触发onmessage(),他把返回的数据(字符串)保存在event.data属性中。
websockets其他事件:

  • open,成功建立时触发
  • error,发生错误时触发,连接不能持续
  • close,连接关闭时触发
    websockets不支持DOM 2级事件监听。因此必须用DOM 0分别定义每个事件
socket.onopen = function(){};
socket.onerror = function(){};
socket.onclose = function(event){
     
    console.log(event.wasClean + event.code + event.reason);
}

如上,是定义方法,而且可以看出只有onclose接收一个对象,对象有三个属性,分别表示:

  1. wasClean 是一个布尔值,表示连接是否已经明确关闭
  2. code 服务器返回的数值状态码
  3. reason 字符串,包含服务器发来的消息

小结

本章涉及的内容很多,书后面的总结可以帮我们理一理思路,
Ajax 、comet,红宝书_第15张图片

你可能感兴趣的:(JS基础学习,ajax,js,comet)