《高性能javascript》读书笔记
2012-06-04 16:19 臭小子1983 阅读(462) 评论(0) 编辑 收藏 举报网站的缺陷:
1、<script>标签出现在文档主体中
2、依赖于浏览器嗅探,而不是通过能力检测来测试javascript功能的兼容
3、在锚元素的href属性使用硬编码的javascript:前缀
4、多余的、重复的、高度定制的javascript代码
Yahoo前端优化十四条军规:
1、将脚本放到</body>之前,不放在<head>标签内.
第一章 加载和执行
一、页面因<script>标签而等待
如果页面中加入<script>标签,这时候就行选执行script脚本,而HTML代码处于等待状态,等待javascript运行完后在进行渲染
1 <body> 2 adfasdfasdfadfasdfsdf // 第一步显示 3 <script> 4 var n = 0; // 第二步执行javascript代码 5 for(var i=0; i<100000; i++){ 6 if(i==50){ 7 document.write("<br>"); 8 } 9 else{ 10 document.write((n+=i)) 11 } 12 } 13 </script> 14 15 <p>aaaaaaaaaaaabbbbbbbbbbbb</p> // 等待JS执行完后在执行下面的内容 16 <img scr="http://i1.itc.cn/20140404/2e7c_58ba90cc_9e59_3e75_a20f_a0c631c8035d_1.jpg" width="100%" height="800px"> 17 </body>
注:如果将js代码加入window.onload中就是表示页面所有加载完后成,在执行js代码,这样就不会存在阻塞的情况.
二、src属性来调用外部文件
如果载入外部js文件,也会停下来先解析js文件,然后在加载下面的HTML代码
如果把三个<script>放到head标签中,先会去解析script标签,这样如果网速慢就会出现页面先是空白,等待js解析,
现代浏览器可以让js文件并行下载,但对页面html代码阻塞还是存在的,所以在前端优化其中之一就是将js文件尽可能的放到</body>之前
三、非阻塞脚本
非阻塞脚本在于等页面下载完后在加载脚本,也就是window的load事件发出之后在开始执行代码。
1 </script> 2 3 <script> 4 function loadScript(url, callback){ 5 var script = document.createElement("script"); 6 if(script.readyState){ // ie 7 scrip.onreadystatechange = function(){ 8 if(script.readyState === "loaded" || script.readyState === "complete"){ 9 scrip.onreadystatechange = null; 10 callback; 11 } 12 } 13 } 14 else{ 15 script.onload = function(){ 16 callback 17 } 18 } 19 20 script.src = url; 21 document.getElementsByTagName("body")[0].appendChild(script); 22 } 23 </script>
四、无阻塞脚本的四种方法:
无阻塞脚本的秘诀在于,在页面加载完成后才加载javascript代码。也就是说在windows对象的load事件触发后再下载脚本。几种方式:
1,用扩展属性:defer
defer指明了脚本不会修改dom,因此代码能安全地延迟执行,下载和执行都不会阻塞页面渲染,一些浏览器不被支持,所以他不是一个完美的方法.
示例:
<script type="text/javascript" src="file1.js" defer></script>
或
<script defer>
alert("晚于没有defer标记的脚本执行,早于window.onload执行");
</script>
缺点:只有IE4+和Firefox3.5+支持
2,动态脚本元素:通过DOM(文档对象模型) 可以用javascript动态创建包括<script>标签在内的所有元素。
示例:
1 <script> 2 var script=document.createElement("script"); 3 script.type="text/javascript"; 4 script.src="file1.js"; 5 document.getElementByTagName("head")[0].appendChild(script);//此处是加到head 6 </script>
3、减少页面加载时的阻塞
使用此种方式下载文件时,返回的代码会立即执行(firefox和opera会等待此前所有动态脚本节点执行完毕)。
firefox,opera,chrome和safari3以上版本会在<script>元素接收完成时触发一个load事件:
示例: script.onload=function(){alert("接收完成");};
IE会触发一个readystatechange事件,<script>元素提供readystate属性(uninitialized:初始状态 ,loading:开始下载,loaded:下载完成,interactive:数据完成下载但尚不可用,complete:所有数据已准备就续)
示例:
1 script.onreadystatechange = function(){ 2 3 if(script.readystate="loaded"||script.readystate=="complete"){ //一般只要判断这两种情况 4 script.onreadystatechange=null; //这里必须删除事件,以确保事件不会处理两次 5 alert("接收完成"); 6 } 7 };
1 function loadScript(url, callback){ 2 var script = document.createElement ("script") 3 script.type = "text/javascript"; 4 if (script.readyState){ //IE 5 script.onreadystatechange = function(){ 6 if (script.readyState == "loaded" || script.readyState == "complete"){ 7 script.onreadystatechange = null; 8 callback(); 9 } 10 }; 11 } 12 else { //Others 13 script.onload = function(){ 14 callback(); 15 }; 16 } 17 script.src = url; 18 document.getElementsByTagName_r("head")[0].appendChild(script); 19 }
这里提供一个供标准及IE共用的方法
使用时直接调用就可以了
1 loadScript("file1.js",function(){ 2 alert("加载完成"); 3 });
或者串联起来按顺序下载多个
1 loadScript("file1.js",function(){ 2 loadScript("file2.js",function(){ 3 alert("加载完成"); 4 }); 5 });
四、XMLHttpRequest脚本注入:
发送一个get请求,然后把获取到的响应作为script的内容,再用DOM加到head中。
这个的缺点是不能请求跨域的js文件,所以通常不用它。
五、推荐的无阻塞模式
A,用上面所述的动态loadScript方法加载界面渲染必须的脚本,在加载完成的事件里再加载其它脚本,并且放在</body>闭合标签之前。建议用YUI Compressor把脚本进行压缩。
B,LazyLoad类库 http://github.com/rgrove/lazyload 1.5K
示例:
1 <script type="text/javascript" src="lazyload-min.js"></script> 2 <script type="text/javascript"> 3 LazyLoad.js(["file1.js","file2.js"],function(){ //如果只加载一个文件的话不需要中括号 4 Application.init(); 5 }); 6 </script>
C,LABjs类库 http://labjs.com
示例:
1 <script type="text/javascript" src="lab.js"></script> 2 <script type="text/javascript"> 3 $LAB.script("file1.js").wait() //用这个wait能保证先执行完再下载file2.js。下面的file2.js和file3.js不一定谁先下载 4 .script("file2.js") 5 .script("file3.js") 6 .wait(function(){ 7 Application.init(); 8 }); 9 </script>
第二章 加载和执行
一、javascript中四种基本的数据存取位置:
1、直接量:只代表自己,不存储在特定位置,包括字符串、数字、布尔值、对象、数组、函数、正则表达式、null值、undefined值
2、变量:var定义的数据存储单元
3、数组元素:存储在数组内部,以数字作为索引
4、对象成员:存储在对象内部,以字符串作为索引
二、代码中的性能影响
1、直接量和局部变量的访问速度快于数组项和对象成员的访问速度。
2、局部变量比域外部变量访问要快,因为它位于作用域的第一个对象中
3、变量在作用域中越深,访问速度越慢,全局变量总是最慢的,因为它是位于作用域链的最后一环
4、嵌套对象成员会造成重大性能影响,尽量小用.
5、一个属性或方法在原型链中越深访问越慢
通过这种方法可以提高javascript的访问性能:经常用的对象成员、数组项、和域外的变量存入局部变量中,因为访问局部变量要高于原始的变量
三、一个函数被创建的作用域中对象的集合叫作函数的作用域链(键值对)
其中包括一个单独的可变对象(window,navigator,document等等)。
执行函数时,由运行期上下文(函数执行的环境)按对象在函数中出现的顺序生成活动对象(包括所有局部变量、命名参数、参数集合、this),然后将活动对象推入作用域链的前端。
函数访问数据时都需要去搜索运行期上下文的作用域链,查找同名的标识符,搜索过程从作域链头部开始,也就是当前运行函数的活动对象,直到找到为止。如果到最后还没找到,则认为该标识符是未定义的。并且,假如有多个同名的标识符的话,只会查找第一个。函数中读写局部变量是最快的,而读写全局变量是最慢的。
如果某个跨作用域的值在函数中被引用一次以上,那么建议把它存储到局部变量里。
With(document等):把document等括号内的对象置 于作用域的头部,会使其它局部变量标识符的识别。
catch: 把异常对象置于作用域的头部,同样会影响局部变量标识符的识别。
eval(code) 执行代码。代码格式为字符串。
四、闭包、作用域和内存:
1、什么是闭包
允许函数访问局部变量,
function getAdd(){
var a = 10, b = 20;
var ss = function(){
return a+b;
}
}
闭包的[[Scope]]属性包含了与运行期上下文作用域相同的对象和引用,因为会有一项副作用,激活对象无法被销毁。占内存,可能会导致内存泄露。
对象成员包括属性和方法。
原型,我感觉相当它的父类。在Firefox、Safari、Chrome中这个属性_proto_即表示它的原型。
1 var book ={ 2 title:"标题"; 3 publisher:"雅虎" 4 } 5 alert(book.hasOwnProperty("title"));//true 表示title是实例成员 6 alert(book.hasOwnProperty("toString"));//false 表示该实例中没有toString 7 8 alert("title" in book);//true 9 10 alert("toString" in book);//true 都返回true,表示用in操作符时会搜索实例也搜索实例的原型。
对象的原型决定了实例的类型。所有对象都是对象Object的实例。
1 function Book(title,publisher){ 2 this.title=title; 3 this.publisher=publisher; 4 } 5 6 Book.prototype.sayTitle=function(){ 7 alert(this.title); 8 }; 9 10 var book1=new Book("标题","雅虎"); 11 var book2=new Book("测试","哈哈"); 12 alert(book1 instanceof Book);//true 13 alert(book1 instanceof Object);//true
上面的示例中,book1的原型(_proto_)是Book.prototype, Book.prototype的原型是Object.
原型链越深,搜索实例成员越慢。
嵌套成员: 每次遇到点操作时,导致js引擎搜索所有对象成员。所以location.href比window.location.href要快。 object.name和object["name"]功能差不多(Safari中点符号会快些)。
不要在同一个函数里多次查找同一个对象成员,除非它的值改变了。应该用查找一次保存为局部变量。
第三章 DOM编程
一、本章讨论三个问题:
1、访问和修改DOM元素
2、修改DOM元素样式,会造成页面重绘和重排
3、通过DOM处理用户响应
二、什么叫DOM,它为什么慢
DOM(文档对象模型),它是一个独立的语言,主要用来操作html页面的元素
DOM和javascript(ECMAScript)通过接口连接,每次ECMAScript访问DOM时,就会产生性能问题,所以访问和修改DOM会影响性能。
示例:
1 例1: 2 <script> 3 var sd = new Date() 4 var startTime = sd.getTime(); 5 6 for(var i=0; i<1500; i++){ 7 document.getElementById("box").innerHTML += i + "<br>"; 8 } 9 10 11 var ed = new Date() 12 var endTime = ed.getTime(); 13 alert(endTime - startTime); 14 </script> 15 16 17 例2:改进后的代码 18 <script> 19 var sd = new Date() 20 var startTime = sd.getTime(); 21 22 var addHTML = ""; 23 for(var i=0; i<1500; i++){ 24 addHTML += i + "<br>"; 25 } 26 document.getElementById("box").innerHTML = addHTML; 27 28 var ed = new Date() 29 var endTime = ed.getTime(); 30 alert(endTime - startTime); 31 </script> 32 33 // 例2中将操作DOM放到了循环外,循环中将数据赋给一个变量最后将操作一个DOM, 34 35 例1:代码运行的时间是3817毫秒 例2所用的是7毫秒 36 37 所以尽量减少DOM的操作
二、HTML集合包括:
1 document.getElementsByName(); 2 document.getElementsByClassName(); 3 document.getElementsByTagName(); 4 document.images 5 document.links 6 document.forms 7 document.forms[0].elements
html集合是类似数组的列表(没有push()或slice()之类的方法所以不是数组)。但是能以数字索引的方式访问列表中的元素,还有length属性。
html集合非常低效,因为它是实时和文档保持一致的,包括访问length属性都会重复执行查询的过程。
比如下面的就是一个死循环
1 var alldivs=document.getElementsByTagName("div"); 2 for(var i=0;i<alldivs.length;i++){ //因为每次执行了appendChild后这个length都加了1,所以是个死循环。并且访问length属性性能也很低。 3 document.body.appendChild(document.createElement("div"); 4 }
一般解决上面性能慢的方式,是设置一个集合,并把它拷贝到一个数组中,下面的可为通用转换函数:
1 function toArray(coll){ 2 for(var i=0, a=[],len=coll.length;i<len;i++){ 3 a[i]=coll[i]; 4 } 5 }
三、在DOM中爬行,也就是从某个DOM元素开始操作周围的元素,或者递归查找所有的子节点,建议使用nextSibling,别用childNodes
下面表格中建议用第一列的DOM属性,性能好的属性名 被替换的属性名
children childNodes
childElementCount childNodes.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling
querySelectorAll方法:可进行组合查询。此种方法反回的是数组对象,不是html集合,因此返回的节点不会对应实时的文档结构,避免了html集合引起的性能问题。支持的浏览器有IE8,Firefox3.5,Safari3.1,Chrome1,Opera.
var elements = document.querySelectorAll("#menu a");
四、重绘和重排版
当浏览器下载完所有HTML标记(html、js、css、图片),
创建两个内部数据结构:1、一棵DOM树 2、一棵渲染树
1、DOM树:文档中的每一个节点
2、渲染树:为DOM树的每一个节点存放css模盒,如:边框、填充、边距、和位置等,DOM树和渲染树加载完后浏览器就可以显示页面上的元素了
重绘、重排版:当使用DOM来改变元素的变化(宽、高、位置),浏览器使渲染树受到影响,需要重绘渲染树,这个过程叫“重绘",这个过程叫重排版
不是所有DOM操作都会导致重排版,如改变背景颜色而不需要改变其它元素的宽、高度这种情况只需要重绘,而不需要重排版。
五、减少重绘和重排版
重绘和绘排版都是可能导致网页失去响应,所以尽量减少重绘和重排
发生重排版的情况:
1、增减DOM元素 2、元素位置的改变 3、元素尺寸的改变(边框、填充、边框宽度、宽高度) 4、内容改变(文字或图片被另一个所替换) 5、最初页面的渲染 6、浏览器窗口的改变
总结:
1、尽可能减少DOM的操作,尽可能使用javascript完成
2、反复重复的地方使用局部变量来存放DOM的引用
3、如果一个元素信息,将Lenght缓存到一个变量中,在迭代使用这个变量,如果经常使用这个集合可以存到数组中.
4、重绘和重排,批量修改风格,离线操作DOM树,减少布局访问的信息
六、HTML中插入script能快速影响用户界面
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>无标题文档</title> 6 </head> 7 8 <body> 9 <div class="ds"> 10 <script> 11 document.write("aaaaaa"); // 在这个节点上浏览器停止渲染HTML文档,将Script交由js引擎编译执行 12 </script> 13 </div> 14 </body> 15 </html>
阻塞:js的执行会中断html的渲染
单线程的UI渲染
避免阻塞:异步执行javascript代码
七、DOM为什么会慢
DOM与ECMAScript之间是两个不同,每次都需要消耗些时间
第四章 算法和流控制
代码整体结构是执行速度的因素之一,代码量少不一定执行速度快,代码量多不一定执行速度慢.
一、循环
Javascript定义了四种类型的循环,
1、for:由四部分组成(初始化、前测条件、执行体、循环体)
2、while循环:单体的预测循环,执行前先对条件进行计算,如果不满足条件不执行循环体.
var i=0; while(i<10) { i++ }
3、do.. while循环:后测试的循环,循环体先执行一次,在去看是否满足条件是否应在执行
var i=0; do{ i++; } while(i<10);
4、for...in循环:可以循环枚举和对象的命名和属性
四种循环for...in循环比较慢
二、条件表达式
if...else和switch
如果条件数量较多推荐使用switch,switch要比if...else执行的数度要快,这要在条件较多时才能体现.
第五章 字符串和正则表达式
一、字符串的连接
例:str += 'one' + 'two'; // 结束是str="onetwo"
这个例子发生了四个步骤:1、在内存中创建一个str 2、将'onetwo'赋给str 3、临时字符串与str相连 4、结果赋给str
修改:
str += 'one';
str += 'two'
这样能提高10%-40%,str = str + 'one' + 'two';
第六章 响应接口
浏览器线程
浏览器有一个独立的处理程序的进程,由两个任务所共享:1、用户界面更新任务 2、javascript任务
当javascript运行时用户界面不能对输入产生反应
<button onclick="handleClick()">Click Me</button> <script type="text/javascript"> function handleClick(){ var div = document.createElement("div"); div.innerHTML = "Clicked!"; document.body.appendChild(div); } </script>
// 当按钮按下时触发UI线程创建两个任务,1、按钮的UI更新 2、javascript的执行
第七章 Ajax 异步javascript和XML
一、有五种常用技术用于向服务器请求数
1、XMLHttpRequest(XHR):异步收发数据
能够控制收发的请求和数据,也可以向请求报文中添加任意信息和参数,并从服务器返回头信息
1 var url='/index.ashx'; 2 var params=['id=434','limit=43'] 3 var req=new XMLHttpRequest(); 4 5 req.onreadystatechange=function(){ 6 if(req.readyState===4){//4表示整个响应已接收完成,3表示正在与传输的服务器响应进行交互,低版本的IE不支持3 7 var responseHeaders = req.getAllResponseHeaders();//获取响应头信息 8 var data = req.responseText;//获取数据 9 //处理数据 10 } 11 } 12 13 req.open('GET',url+'?'+params.join('&'),true); 14 req.setRequestHeader('X-Requested-With','XMLHttpRequest');//设置请求头信息. 15 req.send(null);//发送一个请求
不能使用XHR从外域请求数据
从服务器传回的数据被当做字符串或者XML对象,所以处理大量数据将会很慢
对于不会改变服务器状态,只会获取数据(被称为'幂等行为')的请求,应该使用GET.经GET请求的数据会被缓存起来,如果需要多次请求同一数据它会有助于提升性能.
当请求的URL加上参数的长度接近或超过2048个字符时应该使用POST获取数据.因为IE限制URL长度.
2、Dynamic script tag insertion 动态脚本注入
优势:能跨域请求数据.
速度快,响应消息是作为js执行,而不是作为字符串需要进一步处理
缺点:不能设置请求的头信息
参数传递只能使用GET方式
不能设置请求的超时处理或重试
就算请求失败也无法知道
必须等待所有的数据都已返回才能访问它们
不能访问请求的头信息,不能把整个响应作为字符串来处理
响应消息作为脚本标签的源码,必须是可执行的JS代码,不能使用纯XML,纯JSON或其它任何格式的数据,无论哪种格式都必须封闭在一个回调函数中.
注意:使用这种技术从那些无法直接控制的服务器上请求数据时需要小心,JS没有任何权限和访问控制的概念,因此使用动态脚本注入添加到页面中的任何代码都可以完全控制整个页面.包括修改任何内容,把用户重定向其它网站,甚至跟踪用户在页面上的操作并发送数据到第三方服务器.
1 var scriptElement=document.createElement('script'); 2 scriptElement.src='http://anyurl/lib.js'; 3 document.getElementsByTagName('head')[0].appendChild(scriptElement); 4 function jsonCallback(jsonString){ 5 var data=eval('('+jsonString+')'); 6 //处理数据 7 } 8 9 //lib.js文件需要把数据封装在jsonCallback函数中 10 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传回的键值对数据.
1 var url='/index.ashx'; 2 var params=['step=2','time=3223323']; 3 var img=new Image(); 4 img.src=url+'?'+params.join('&'); 5 img.onload=function(){ 6 if(this.width==1){ 7 //成功 8 }else if(this.width==2){ 9 //失败,请重试并创建另一个信标 10 } 11 }; 12 13 beacon.onerror=function(){ 14 //出错,稍候重试并创建另一个信标 15 };
服务器会接收到数据并保存下来,它无需向客户端发送任何回馈信息,因此没有图片会实际显示出来.它是给服务器回传信息最快最有效的方式.它的性能消耗很小,而且服务端的错误完全不会影响到客户端.
无法发送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/'];
本地缓存也可以很好地工作在移动设备上.