javascript Ajax 学习

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

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

创建对象

  因为XMLHTTPRequest()是一个构造函数,所以需要实例化一个XMLHttpRequset对象。下面是创建XHR对象的兼容写法;
  如果要建立多个不同的请求,就要实例化多个不同的XMLHttpRequset对象;

  var xhr;
  if(window.XMLHttpRequest){
      xhr = new XMLHttpRequest();
  }else{
      xhr = new ActiveXObject('Microsoft.XMLHTTP');
  }

发送请求

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

open()

  open()方法需要三个参数:xhr.open("GET","test.json",true);
  第一个参数定义发送请求所使用的方法(GET还是POST),不区分大小写,但通常使用大写字母,记得带引号。
  GET用于常规请求,它适用于当URL完全指定请求资源,当请求对服务器没有任何副作用以及当服务器的响应是可缓存的情况下。
  然而,在以下情况中,请使用POST请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST没有数据量限制)
  • 发送包含未知字符的用户输入时,POSTGET更稳定也更可靠
      第二个参数规定服务器端脚本的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末尾添加一个随机数或时间戳。

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

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请求的两倍。

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

接收响应

  一个完整的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属性为01的情况。

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

  如果将open()方法的第三个参数设置为false,接收的就是同步响应,那么send()方法将阻塞直到请求完成。一旦send()返回,仅需要检查XHR对象的statusresponseText属性即可。
  应该避免使用同步请求。客户端javascript是单线程的,当send()方法阻塞时,它通常会导致整个浏览器UI冻结。如果连接的服务器响应慢,那么用户的浏览器将冻结,用户体验非常不好。

进度事件

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

  有以下6个进度事件:

  • loadstart: 在接收到响应数据的第一个字节时触发
  • progress: 在接收响应期间持续不断地触
  • error: 在请求发生错误时触发
  • abort: 在因为调用abort()方法而终止连接时触发
  • load: 在接收到完整的响应数据时触发
  • loadend: 在通信完成或者触发error、abort或load事件后触发
  • timeout: 超时发生时触发
      每个请求都从触发loadstart事件开始,接下来,通常每隔50毫秒左右触发一次progress事件,然后触发load、error、abort或timeout事件中的一个,最后以触发loadend事件结束

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

load

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

progress

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

upload上传进度

  除了为监控HTTP响应的加载定义的这些有用的事件外,XHR2也给出了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象将有upload属性。upload属性值是一个对象,它定义了addEventListener()方法和整个progress事件集合,比如onprogressonload。但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事件。

  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("请求结束");    
  }

封装

    ajax({
      url: "./TestXHR.aspx", //请求地址
      type: "POST", //请求方式
      data: {
        name: "super",
        age: 20
      }, //请求参数
      dataType: "json",
      success: function (response, xml) {
        // 此处放成功后执行的代码
      },
      fail: function (status) {
        // 此处放失败后执行的代码
      }
    });

    function ajax(options) {
      options = options || {};
      options.type = (options.type || "GET").toUpperCase();
      options.dataType = options.dataType || "json";
      var params = formatParams(options.data);

      //创建 - 非IE6 - 第一步
      if (window.XMLHttpRequest) {
        var xhr = new XMLHttpRequest();
      } else { //IE6及其以下版本浏览器
        var xhr = new ActiveXObject('Microsoft.XMLHTTP');
      }

      //接收 - 第三步
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          var status = xhr.status;
          if (status >= 200 && status < 300) {
            options.success && options.success(xhr.responseText, xhr.responseXML);
          } else {
            options.fail && options.fail(status);
          }
        }
      }

      //连接 和 发送 - 第二步
      if (options.type == "GET") {
        xhr.open("GET", options.url + "?" + params, true);
        xhr.send(null);
      } else if (options.type == "POST") {
        xhr.open("POST", options.url, true);
        //设置表单提交时的内容类型
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(params);
      }
    }
    
    //格式化参数
    function formatParams(data) {
      var arr = [];
      for (var name in data) {
        arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
      }
      arr.push(("v=" + Math.random()).replace(".", ""));
      return arr.join("&");
    }
posted @ 2018-03-14 10:03  快乐锁  阅读(122)  评论(0编辑  收藏  举报