jacascript AJAX 学习

前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!

  AJAX 是 asynchronous javascript and XML 的简写,就是异步的 javascript 和 XML。这一技术能够向服务器请求额外的数据而无须刷新整个页面,会带来更好的用户体验。虽然名字中包含 XML,但是 ajax 通信与数据格式无关;

创建对象

  因为 XMLHTTPRequest() 是一个构造函数,所以需要实例化一个 XMLHttpRequset 对象。下面是创建XHR对象的兼容写法;

  如果要建立多个不同的请求,就要实例化多个不同的 XMLHttpRequset 对象;

        <script type="text/javascript">
            var xhr;
            if(window.XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject('Microsoft.XMLHTTP');
            }
        </script>

发送请求

  要想把请求发送到服务器,我们就需要使用 open() 方法和 send() 方法。

open()

  open() 方法需要三个参数:

       xhr.open("GET","test.json",true);

  第一个参数定义发送请求所使用的方法("GET" 还是 "POST"),不区分大小写,但通常使用大写字母,记得带引号。

  "GET" 用于常规请求,它适用于当 URL 完全指定请求资源,当请求对服务器没有任何副作用以及当服务器的响应是可缓存的情况下。

  然而,在以下情况中,请使用 "POST" 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据("POST" 没有数据量限制)
  • 发送包含未知字符的用户输入时,"POST" 比 "GET"更稳定也更可靠

  第二个参数规定服务器端脚本的 URL(该文件可以是任何类型的文件,比如 .txt 和 .xml,或者服务器脚本文件,比如 .asp 和 .php (在传回响应之前,能够在服务器上执行任务)。

  第三个参数规定是否异步发送请求的布尔值,如果不填写,默认为 true,表示异步发送。如果接受的是同步响应,则需要将 open()方法的第三个参数设置为 false,那么 send()方法将阻塞直到请求完成。客户端 javascript 是单线程的,当 send() 方法阻塞时,它通常会导致整个浏览器界面冻结。如果连接的服务器响应慢,那么用户的浏览器将冻结,所以应该避免使用同步。

send()

  send() 方法接收一个参数,即要作为请求主体发送的数据。调用 send() 方法后,请求被分派到服务器。

  如果是 "GET" 方法,send() 方法无参数,或参数为 null;如果是 "POST" 方法,send() 方法的参数为要发送的数据。

            xhr.open("GET","test.json",false);
            xhr.send(null);

GET

  "GET" 用于常规请求,它适用于当 URL 完全指定请求资源,当请求对服务器没有任何副作用以及当服务器的响应是可缓存的情况下。

  【数据发送】

  使用 "GET" 方式发送请求时,数据被追加到 open() 方法中 URL 的末尾,可以直接看到,存在安全隐患。

  数据以问号开始,名和值之间用等号链接,名值对之间用和号(&)分隔。使用 "GET" 方式发送的数据常常被称为查询字符串。

  【编码】

  由于 URL无法识别特殊字符,所以如果数据中包含特殊字符(如中文),则需要进行编码,编码的方式有很多种,其中 encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。该方法主要对 ;/?:@&=+$,# 等这些用于分隔URI组件的字符以及中文进行编码。由于此方法对 :/ 都进行了编码,所以不能用它来对网址进行编码,而适合对 URI 中的参数进行编码

  在 "GET" 请求中,为了避免缓存的影响,可以向 URL 末尾添加一个随机数或时间戳。

    <script>
        var url = 'test.php' +'?name=' + encodeURIComponent("你好");
        xhr.open('GET',url+'&'+Number(new Date()),true);
        xhr.send(null);
    </script>

POST

  "POST" 请求通常用于向服务器发送应该被保存的数据。"POST" 方法常用于 HTML 表单。它在请求主体中包含额外数据且这些数据常存储到服务器上的数据库中。

  在 open() 方法第一个参数的位置传入"POST",就可以初始化一个 "POST" 请求。

  【设置请求头】

  默认情况下,服务器对 "POST" 请求和提交表单的请求并不会一视同仁。因此,服务器端必须有程序来读取发送过来的原始数据,并从中解析出有用的部分。不过,可以使用 XHR 来模仿表单提交:首先将 content-Type 头部信息设置为 application/x-www-form-urlencoded,也就是表单提交时的内容类型;

  使用 setRequestHeader() 方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称头部字段的值。要成功发送请求头部信息,必须在调用 open()方法之后且调用 send() 方法之前调用 setRequestHeader() 方法 。

  在项目中,又是需要验证用户登录,可以设置请求头验证。机制就是:在用户首次登录成功之后,服务器发送token到客户端,客户端存入cookie。用户做任何请求操作时,在 ajax的请求头里带上 token,用以 server-end 做登录状态验证。

  【发送主体】

  发送 "POST" 请求的第三步就是向 send() 方法中传入某些数据,这一点和 "GET" 请求不一样。由于 XHR 最初的设计主要是为了处理 XML,因此可以在此传入 XML DOM 文档,传入的文档经序列化之后将作为请求主体被提交到服务器。当然,也可以在此传入任何想发送到服务器的字符串。

  接下来要以适当的格式创建一个字符串,并使用 send() 方法发送。

  "POST" 数据的格式与 "GET" 数据的格式相同,名和值之间用等号链接,名值对之间用和号(&)分隔。

  【编码】

  由于使用 "POST" 方式传递数据时,需要设置请求头 "content-type",这一步骤已经能够自动对特殊字符(如中文)进行编码,所以就不再需要使用 encodeURIComponent() 方法了。

  "POST" 请求主要用于数据提交,相同 URL 的重复 "POST" 请求从服务器得到的响应可能不同,所以不应该缓存使用 "POST" 方法的请求。

  "GET" 对所发送信息的数量有限制,一般在2000个字符。与 "GET" 请求相比,"POST" 请求消耗的资源会更多一些。从性能角度来看,以发送相同的数据计,"GET" 请求的速度最多可"POST"请求的两倍。

    <script>
        xhr.open('POST',url,true);
        //设置请求头
        xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
        //拼接数据
        var strData = 'name="abc"&num=123';
        //发送请求
        xhr.send(strData);
    </script>

接收响应

  一个完整的 HTTP 响应由状态码、响应头集合和响应主体组成。

  在收到响应后,这些都可以通过 XMLHttpRequset 对象的属性和方法使用,主要有以下4个属性:

responseText 作为响应主体被返回的文本(文本格式)
responseXML 如果响应的内容类型是 "text/xml" 或 "application/xml",这个属性中将保存着响应数据的 XML DOM 文档(document 格式)
status HTTP状态码(数字形式)
statusText HTTP状态说明(文本格式)

  在接收到响应后,第一步是检查 status 属性,以确定响应已经成功返回。一般来说,可以将 HTTP 状态码为 200 作为响应成功的标志。此时,responseText 属性的内容已经就绪,而且在内容类型正确的情况下,responseXML 也可以访问了。此外,状态码为 304 表示请求的资源并没有被修改,可以直接使用浏览器中缓存的版本;当然,也意味着响应是有效的。

  无论内容类型是什么,响应主体的内容都会保存到 responseText 属性中,而对于非 XML 数据而言,responseXML 属性的值将为 null;

            if((xhr.status >=200 && xhr.status < 300) || xhr.status == 304){
                alert(xhr.responseText);
            }else{
                alert('请求失败,响应代码为:' + xhr.status);
            }

异步响应和同步响应

  如果不设置 open() 方法的第三个参数(默认为true,即异步响应)。

  如果接收的是异步响应,这就需要检测 XMLHttpRequset 对象的 readyState 属性,该属性表示 请求/响应 过程的当前活动阶段。这个属性可取的值如下:

0(UNSENT) 未初始化 还没调用open()
1(OPEND) 启动 已经调用open() ,但还没调用 send()
2(HEADERS_RECEIVED) 发送 己经调用 send() 方法,且接收到头信息。
3(LOADING) 正在接收 已经接收到部分响应主体信息。
4(DONE) 完成 已经接收到全部响应数据,而且已经可以在客户端使用了。

  理论上,只要 readyState 属性值由一个值变成另一个值,都会触发一次 readystatechange 事件。可以利用这个事件来检测每次状态变化后 readyState 的值。通常,我们对readyState 值为4的阶段感兴趣,因为这时所有数据都已就绪。

  必须在调用 open() 之前指定 onreadystatechange 事件处理程序才能确保跨浏览器兼容性,否则将无法接收 readyState 属性为0和1的情况 。

            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status == 200){
                        alert(xhr.responseText);
                    }
                }
            }

  如果将 open() 方法的第三个参数设置为 false,接收的就是同步响应,那么 send() 方法将阻塞直到请求完成。一旦 send() 返回,仅需要检查 XHR对象的 status 和responseText 属性即可。

  应该避免使用同步请求。客户端 javascript 是单线程的,当 send() 方法阻塞时,它通常会导致整个浏览器 UI 冻结。如果连接的服务器响应慢,那么用户的浏览器将冻结,用户体验非常不好。

进度事件

  一般地,使用 readystatechange 事件探测 HTTP 请求的完成。XHR2 规范草案定义了进度事件 Progress Events 规范,XMLHttpRequest 对象在请求的不同阶段触发不同类型的事件,所以它不再需要检査 readyState 属性。

  有以下6个进度事件:

  1. loadstart: 在接收到响应数据的第一个字节时触发
  2. progress: 在接收响应期间持续不断地触
  3. error: 在请求发生错误时触发
  4. abort: 在因为调用abort()方法而终止连接时触发
  5. load: 在接收到完整的响应数据时触发
  6. loadend: 在通信完成或者触发error、abort或load事件后触发
  7. timeout: 超时发生时触发

  每个请求都从触发 loadstart 事件开始,接下来,通常每隔50毫秒左右触发一次 progress 事件,然后触发 load、error、abort 或 timeout 事件中的一个,最后以触发 loadend 事件结束

  对于任何具体请求,浏览器将只会触发 load、abort、timeout 和 error 事件中的一个。XHR2规范草案指出一旦这些事件中的一个发生后,浏览器应该触发 loadend 事件。

load

  响应接收完毕后将触发 load 事件,因此也就没有必要去检查 readyState 属性了。但一个完成的请求不一定是成功的请求,例如,onload 事件的处理程序应该检查 XMLHttpRequest 对象的 status 状态码来确定收到的是“200 OK”而不是“404 Not Found”的HTTP响应

progress

  progress 事件会在浏览器接收新数据期间周期性地触发。而 onprogress 事件处理程序会接收到一个 event 对象,其 target 属性是 XHR 对象,但包含着三个额外的属性:lengthComputable、loaded 和t otal。其中,lengthComputable 是一个表示进度信息是否可用的布尔值,loaded 表示已经接收的字节数,total 表示根据 Content-Length 响应头部确定的预期字节数。有了这些信息,就可以为用户创建一个进度指示器了。

上传进度upload

  除了为监控 HTTP 响应的加载定义的这些有用的事件外,XHR2 也给出了用于监控 HTTP 请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest 对象将有 upload 属性。upload 属性值是一个对象,它定义了 addEventListener() 方法和整个 progress 事件集合,比如 onprogress 和 onload。但 upload 对象没有定义 onreadystatechange 属性,upload  仅能触发新的事件类型。

    <input type="file" name="file1" id="file1" style="display:none">
    <button id="btn">上传文件</button>
    <div id="pro"></div>
    <div id="result"></div>
    <script>
        btn.onclick = function(){
            file1.click();
            pro.innerHTML = result.innerHTML = '';
        }
        file1.onchange = function(){
            //创建xhr对象
            var xhr = new XMLHttpRequest();
            var data = file1.files[0];
            //上传事件
            xhr.upload.onprogress = function(e){
                e = e || event;
                if (e.lengthComputable){
                    pro.innerHTML = "上传进度为:" + e.loaded + " of " + e.total + " bytes" + ';百分比为:' + e.loaded/e.total;
                }
            }
            xhr.onload = function(e){
                var data = xhr.responseText;
                e = e || event;
                if(xhr.status == 200){
                    result.innerHTML =  data;
                }
            };
            //发送请求
            xhr.open('post','pp.php',true);
            xhr.setRequestHeader("content-type",data.type);
            xhr.send(data);
        }
        </script>

超时、中止、错误事件

  HTTP 请求无法完成有3种情况。如果请求超时,会触发 timeout 事件。如果请求中止,会触发 abort 事件。最后,像太多重定向这样的网络错误会阻止请求完成,但这些情况发生时会触发 error 事件。

  可以通过调用 XMLHttpRequest 对象的 abort() 方法来取消正在进行的 HTTP 请求。调用 abort() 的主要原因是完成取消或超时请求消耗的时间太长或当响应变得无关时。

  XHR对象的 timeout 属性等于一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。该属性默认等于0,表示没有时间限制。如果请求超时,将触发ontimeout 事件。

    <script>
        var xhr = new XMLHttpRequest();
        btn.onclick = function(){
            xhr.abort();
        }
        xhr.onabort = function(){
            console.log("请求已终止");
        }

        xhr.ontimeout = function(){
            console.log('请求超时');
        }
        xhr.timeout = 3000;

        xhr.onerror = function(){
            console.log("请求报错");
        }
        xhr.onloadend = function(){
            console.log("请求结束");
        }
    </script>
上一篇:Codeforces Round#403 (Div. 1)


下一篇:使用doxygen静态分析开源代码