# 7. Ajax

# 数据传输

  1. 有5种常用技术用于向服务器请求数据:
    • XMLHttpRequest(XHR)
    • 动态脚本注入
    • iframes
    • Comet
    • Multipart XHR

现代高性能js中使用的三种技术是XHR、动态脚本注入和multipart XHR。Comet和iframes往往用在极端情况下。

# XMLHTTPRequest

  1. XMLHTTPRequest可以通过readyState值等于3说明此时正在与服务器交互,响应信息还在传输过程中。
if(req.readyState===3){//接收到部分信息,但不是所有

}
1
2
3
  1. 经过GET请求的数据会被缓存起来,如果需要多次请求同一数据,它会有助于提升性能。IE会限制URL长度。

# 动态脚本注入

  1. 这种技术克服了XHR的最大限制:它能跨域请求数据。
  2. 这种技术从哪些你无法直接控制的服务器上请求数据时需要小心。js没有任何权限和访问控制的概念,因此你使用动态脚本注入添加到页面中的任何代码都可以完全控制整个页面。包括修改任何内容,把用户重定向到其他网站,甚至跟踪用户在页面上的操作并发送数据到第三方服务器。引入外部来源的代码时请务必小心。

# Multipart XHR

  1. MXHR允许客户端只用一个HTTP请求就可以从服务器向客户端传送多个资源。它通过在服务端将资源(css文件、HTML片段、js代码、或base64编码的图片)打包成一个由双方约定的字符串分割的长字符串并发送到客户端。然后用js代码处理这个长字符串,并根据mime-type类型和传入的其他‘头信息’解析出每个资源。
  2. 由于MXHR响应消息的体积越来越大,因此有必要在每个资源收到时就立刻处理,而不是等到整个响应消息接收完成。可以通过readyState为3的状态来实现。
if(req.readyState===3&&getLatestPacketInterval==null){
  //开始轮询
  getLatestPacketInterval=window.setInterval(function(){
    getLatestPacket();
  },15)
}
if(req.readyState===4){
  clearInterval(getLatestPacketInterval);
  //获取最后一个数据包
  getLatestPacket()
}
function getLatestPacket(){
  var length=req.responseText.length;
  var packet=req.responseText.substring(lastLength,length);
  processPacket(packet);
  lastLength=length;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

当readyState为3的状态第一次触发是,启动一个定时器,每隔15毫秒检查一次响应中的数据。数据片段会被收集起来,直到发现一个分隔符,然后就把遇到分隔符之前收集的所有数据作为一个完整的资源进行处理。

  1. 这个技术有一些缺点,最大的缺点是这种方式获得的资源不能被浏览器缓存。因为合并后的资源是作为字符串传输的,然后被js代码分解成片段。由于无法用编程的方式向浏览器缓存里注入文件,因此用这种方式获取的资源无法被缓存。另一个缺点是老版本IE不支持readyState为3的状态和data:URL。IE8支持,IE6,7不支持。

  2. HTTP请求是Ajax中最大的瓶颈之一,因此减少其需要的数量会对整个页面的性能有很大的影响。测试显示此技术比各自独立请求快4到10倍。

# 发送数据

当使用XHR发送数据到服务器时,GET方式会更快。对于少量数据而言,一个GET请求往服务器只发送一个数据包。而一个POST请求,至少要发送两个数据包,一个装载头信息,另一个装载POST正文。POST更适合发送大量数据到服务器,因为他不关心额外数据包的数量,另一个原因是IE对URL长度有限制,它不可能使用过长的GET请求。

# Beacons

这项技术类似动态脚本注入。使用js创建一个新的Image对象,并把src属性设置为服务器上脚本的URL,该URL包含了我们要通过GET传回的键值对数据。

var url='/sss.php';
var params=['step=2','time=1234']
var beacon=new Image()
beacon.src=url+'?'+params.join('&');

beacon.onload=function(){
  if(this.width=1){
    //sucess
  }else{
    // error
  }
}
beacon.onerror=function(){}
1
2
3
4
5
6
7
8
9
10
11
12
13

如果你只关心发送数据到服务器(可能需要极少的返回信息),那么请使用图片信标。

# 数据格式

  • XML
  • JSON
  • HTML
  • 自定义格式
  1. 当数据包含数组而不是对象时。JSON有着极好的通用性。

  2. js可以比较快的把一个较大的数据转为简单的HTML,但在服务器处理会快的多。一种可考虑的技术是在服务器上构建好整个HTML再传回客户端,js可以很方便的通过innerHTML属性把它插入页面相应的位置。你应该在客户端的瓶颈是CPU而不是带宽时才使用此技术。

  3. HTML插入DOM的单一操作看似简单,尽管只有一行代码,却需要大量时间把更多数据载入页面。HTML作为一种数据格式,它既缓慢又臃肿。

  4. 当你创建自定义格式时,最重要的决定之一就是采用哪种分隔符。理想情况下,它应当是一个单字符,而且不应该存在于你的数据中。ASCII字符表的前几个字符在大多数服务端语言中能够正常工作而且容易书写。(\u0001)

  5. 对于非常大的数据集,自定义格式是最快的格式,甚至在解析速度和平均加载时间上都能击败本地执行的JSON。当你需要在很短时间内向客户端传送大量数据时可以考虑这种格式。

# Ajax类库的局限

function createXhrObject(){
  var msxml_progid=[
    'MSXML2.XMLHTTP.6.0',
    'MSXML3.XMLHTTP',
    'Microsoft.XMLHTTP',//不支持 readyState 3
    'MSXML2.XMLHTTP.3.0',//不支持 readyState 3
  ];

  var req;
  try {
    req = new XMLHttpRequest();
  } catch(e) {
    for(var i=0,len=msxml_progid.length;i<len;i++){
      try{
        req=new ActiveXObject(msxml_progid[i]);
        break;
      }catch(e2){ }
    }
  } finally {
    return req;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  1. Multipart XHR可以用来减少请求数,并处理一个响应中的各种文件类型,但是它不能缓存接收到的响应。