《高性能javascript》读书笔记:第七章 Ajax
数据传输 |
有五种常用技术用于向服务器请求数据
1,XMLHttpRequest(XHR)
var url='/index.ashx';
var params=['id=434','limit=43']
var req=new XMLHttpRequest();
req.onreadystatechange=function(){
if(req.readyState===4){//4表示整个响应已接收完成,3表示正在与传输的服务器响应进行交互,低版本的IE不支持3
var responseHeaders=req.getAllResponseHeaders();//获取响应头信息
var data=req.responseText;//获取数据
//处理数据
}
}
req.open('GET',url+'?'+params.join('&'),true);
req.setRequestHeader('X-Requested-With','XMLHttpRequest');//设置请求头信息.
req.send(null);//发送一个请求
不能使用XHR从外域请求数据
从服务器传回的数据被当做字符串或者XML对象,所以处理大量数据将会很慢
对于不会改变服务器状态,只会获取数据(被称为'幂等行为')的请求,应该使用GET.经GET请求的数据会被缓存起来,如果需要多次请求同一数据它会有助于提升性能.
当请求的URL加上参数的长度接近或超过2048个字符时应该使用POST获取数据.因为IE限制URL长度.
2,Dynamic script tag insertion 动态脚本注入
优势:能跨域请求数据.
速度快,响应消息是作为js执行,而不是作为字符串需要进一步处理
缺点:不能设置请求的头信息
参数传递只能使用GET方式
不能设置请求的超时处理或重试
就算请求失败也无法知道
必须等待所有的数据都已返回才能访问它们
不能访问请求的头信息,不能把整个响应作为字符串来处理
响应消息作为脚本标签的源码,必须是可执行的JS代码,不能使用纯XML,纯JSON或其它任何格式的数据,无论哪种格式都必须封闭在一个回调函数中.
注意:使用这种技术从那些无法直接控制的服务器上请求数据时需要小心,JS没有任何权限和访问控制的概念,因此使用动态脚本注入添加到页面中的任何代码都可以完全控制整个页面.包括修改任何内容,把用户重定向其它网站,甚至跟踪用户在页面上的操作并发送数据到第三方服务器.
var scriptElement=document.createElement('script');
scriptElement.src='http://anyurl/lib.js';
document.getElementsByTagName('head')[0].appendChild(scriptElement);
function jsonCallback(jsonString){
var data=eval('('+jsonString+')');
//处理数据
}
//lib.js文件需要把数据封装在jsonCallback函数中
jsonCallback({"status":1,"colors":["#fff","#000","#ff0000"]});
3,iframes
4,Comet
5,Multipart XHR
允许客户端只用一个HTTP请求就能从服务端向客户端发送多个资源.
以这种方式获得的资源不能被浏览器缓存.并且IE6和IE7中不支持.
下面两种情况下可考虑使用MXHR,
页面包含了大量其它地方用不到的资源(因此也不需要缓存),尤其是图片.
网站已在每个页面中使用一个独立打包的JS或CSS文件以减少HTTP请求;因为对每个页面来说这些文件都是唯一的,所以并不需要从缓存中读取数据,除非重载页面.
两种广泛的发送数据技术
1,XHR
GET方式发送速度会更快,因为对于少量数据而言,一个GET请求往服务器只发送一个数据包,而一个POST请求至少要发送两个数据包,一个装载头信息,另一个装载POST正文.
POST更适合发送大量数据到服务器,因为它不关心额外数据包的数量 .
2,信标(beacons)
类似动态脚本注入.使用JS创建一个新的Image对象,并把src属性设置为服务器上脚本的URL,该URL包含了我们要通过GET传回的键值对数据.
var url='/index.ashx';
var params=['step=2','time=3223323'];
var img=new Image();
img.src=url+'?'+params.join('&');
img.onload=function(){
if(this.width==1){
//成功
}else if(this.width==2){
//失败,请重试并创建另一个信标
}
};
beacon.onerror=function(){
//出错,稍候重试并创建另一个信标
};
服务器会接收到数据并保存下来,它无需向客户端发送任何回馈信息,因此没有图片会实际显示出来.它是给服务器回传信息最快最有效的方式.它的性能消耗很小,而且服务端的错误完全不会影响到客户端.
无法发送POST数据,而URL的长度有限制.
可以接收服务器返回的数据,但只局限于非常少的几种方式.一种方式是监听Image对象的load事件,它会告诉你服务器是否成功接收数据.另一种方式是检查服务器返回的图片的宽度和高度(如果返回的是图片的话)并使用这些数字通知你服务器的状态.
数据格式 |
1,XML
优点:极佳的通用性
格式严格
易验证
缺点:
相比其它格式,XML极其冗长.每个单独的数据片断都依赖大量结构,所以有效数据的比例非常低.
XML的语法有些模糊,当把一个数据结构转化为XML时,可以把对象参数放到对象元素的属性中,也可放在独立的子元素中,可以使用描述清晰的长标签名,也可以使用高效但难以辨认的短标签名.
语法的解析过程含混,必须提前知道XML响应的布局
解析XML的XPath比getElementsByTagName快许多,但它未得到广泛支持,DOM Level3的XPath已经由FF,Safari,Chrome,Opera实现,IE8有类似的接口,但稍微高级一些.
2,JSON
JSON是一种使用JS对象和数组直接量编写的轻量级且易于解析的数据格式.
可以直接使用eval()来解析JSON字符串.但是在代码中使用eval是很危险的,特别是用它执行第三方的json数据.尽可能使用JSON.parse()方法解析字符串本身.该以捕获JSON中的词法错误,并允许传入一个函数用来过滤或转换解析结果.在FF3.5,IE8及Safari4原生支持,大多数JS库包含JSON解析代码.
3,JSON-P
在使用动态脚本注入时,JSON数据被当成另一个JS文件并作为原生代码执行,为实现这一点,这些数据必须封装在一个回调函数里,这就是所谓有的"JSON填充(JSON with padding)"或JSON-P
它的格式是把JSON当作parseJSON()的参数
最快的JSON格式是使用数组形式的JSON-P
有一种情形要避免使用JSON-P,因为JSON-P必须是可执行的JS,它可能被任何人调用并使用动态脚本注入技术插入到任何网站.而另一方面,JSON在eval前是无效的JS,使用XHR时它只是被当作字符串获取.所以不要把任何敏感数据编码在JSON-P中,因为你无法确认它是否保持着私有调用状态,即使是带有甚至随机URL或做了cookie判读.
4,HTML
服务器端生成HTML返回给客户端,JS使用innerHTML属性把它插入页面相应的位置.
在客户端的瓶颈是CPU而不是带宽时才使用此技术.
5,自定义格式
用数据中不会存在的单字符做分隔.解析时只需要用split()传入字符串或正则表达式进行.
var rows=req.responseText.split(/\u0001/);//正则表达式,IE中的split()会忽略紧挨着的两个分隔符中的第二个分隔符.\u0001是Unicode表示法.
varrows=req.responseText.split("\u0001");//字符串,更为保险
对于非常大的数据集,它是最快的格式.需要在很短的时间内向客户端传送大量数据时可以考虑使用此格式.
Ajax性能指南 |
缓存数据
最快的ajax请求就是没有请求.有两种方法可以避免发送不必要的请求:
1,设置HTTP头信息
如果希望ajax响应能被浏览器缓存,必须使用GET方式发出请求.并且还需要在响应中告诉浏览器应该缓存多久.
一个Expires头信息格式如下:
Expires:Mon,28 Jul 2014 23:30:00 GMT
$lifetime=7*24*60*60;//7天
header('Expires:'.gmdate('D,d M Y H:i:s',time()+$lifetime).'GMT');
2,本地数据存储
把数据从服务器接收后储存起来,把响应文本保存到一个对象中,以URL为键值作为索引.
var localCache={};
function xhrRequest(url,callback){
if(localCache[url]){ //检查此URL的本地缓存
callback.success(localCache[url]);
return;
}
//缓存没找到,则发送请求
var req=createXhrObject();
req.onerror=function(){
callback.error();
};
req.onreadystatechange=function(){
if(req.readyState==4){
if(req.responseText===''||req.status=='404'){
callback.error();
return;
}
localCache[url]=req.responseText;//存储响应文本到本地缓存
callback.success(req.responseText);
}
};
req.open("GET",url,true);
req.send(null);
}
设置Expires头信息是避免发送不必要的请求的更好的方案.但是当用户执行了某些动作可能导致一个或多个已缓存的响应失效,这种情况下用本地缓存非常方便.删除本地缓存十分简单.
delete localCache['/usr/frientlist/'];
本地缓存也可以很好地工作在移动设备上.