首页前端开发HTML【缓存/性能】HTML5缓存的那些事

【缓存/性能】HTML5缓存的那些事

时间2024-01-26 17:37:03发布访客分类HTML浏览539
导读:收集整理的这篇文章主要介绍了html5教程-【缓存/性能】HTML5缓存的那些事,觉得挺不错的,现在分享给大家,也给大家做个参考。小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。 更多前端文章:ht...
收集整理的这篇文章主要介绍了html5教程-【缓存/性能】HTML5缓存的那些事,觉得挺不错的,现在分享给大家,也给大家做个参考。小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。

更多前端文章:http://lvtraveler.gIThub.io/

关于存储

说到存储,你可能会想到这是服务器端的一种设置。

服务器端的存储介质大体上分为4种:

cache:缓存,它可以让从数据库、磁盘上输出的东西/数据放置在缓存里,从而减少数据库或是磁盘的读取与写入(IO)操作; 磁盘文件:如,我们常常会将图片、视频等文件存放在磁盘上; 数据库:MySQL/mongoDB…关系/非关系数据库; 内存:通常放置频繁要使用到的东西,能够提高读取效率;缓存(cache)也是存放在内存里的;

HTML的存储-cookies

在HTML5出生之前,通常在浏览器(客户端)使用cookies来存储客户端的内容;

cookies的特点:

每次的http请求头中,都会带有cookies――缺点; 每个域名只能存储4K大小的cookies; 主域名污染:如果我们使用cookies存储主域名的东西,那么子域名下得Http请求都会带上主域名的东西;

如果关联上网络,那么将带来安全问题。

所以,通常我们会使用cookies用在如购物车、身份验证等问题上。

下面,我们来看一下百度首页的cookies在浏览器端的一个存储形态:

如图:

@H_304_26@

HTTP这一列,如果在setCookie的时候,这里就会打钩,这与httponly相关。vcD4KCjxwPkhUVFBPbmx5o7o8L3A+Cgo8cD7I57n7sNFIVFRQT25secno1sPOqnRydWUsXMfDtGNvb2tpZxpWu8TcsBTzZXJ2ZXK3/s7xxve2y8C0tsHIobvyysfQ3rjEo6y/zbuntsvDu9PQyKjP3r340NC2wcihus3Q3rjEoaPA/cjno6zO0sPh1Nq9+NDQye233dHp1qS1xMqxuvKjrL7Nv8nS1Mq508pv4rj2oam8L3A+Cgo8cD5TZWn1cmWjutPRsLLIq8/gudijrMjnufvJ6NbDwcujrMTHw7TH68fz1rvE3MrHwLTX1EhUVfc808Pcx+vH86GjPC9wPgoKCgo8admgaWQ9"html的存储-userdata"> HTML的存储-UserData 只有IE支持,有微软提供API,但不符合W3C标准; 存储在XML文件中;

HTML5的存储

针对以上问题,HTML5的出现,需要解决以下问题:

解决4K的大小问题; 解决请求头常带存储信息的问题; 解决关系型存储的问题; 跨浏览器平台问题;

HTML5存储形式

本地存储――localstorage /sessionstorage 离线缓存――application cache IndexedDB、Web SQL

本地存储

API:

localstorage 、sessionstorage

存储形式:

keyC> value

过期时间:

localstorage:永久存储,永不失效,除非手动删除
sessionstorage:重新打开页面,或是关闭浏览器,sessionstorage才会消失;

存储大小:
每个域名能存5M;

支持情况:

IE8+,safari3.2+,chrome,firefox等主流浏览器都支持;

使用方法――localstorage/sessionstorage

主要涉及到5个方法:

getItem:获取localstorage/sessionstorage setItem:设置localstorage/sessionstorage removeitem:移除localstorage/sessionstorage key:获取某一个位置上的key值,按值从0开始索引; clear:全部清除localstorage/sessionstorage

例如:我们打开www.baidu.COM

在控制台Console输出面板,输入:

localStorage.setItem("test1","test");
那么在Resources面板的Local Storage下,将出现Key=test1,value=test的记录

localStorage.getItem("test1"); //输出test

localStorage.key(0); //输出BDSUGStoreD

sessionstorage的API与localstorage一样,但是你要注意一点:

sessionStorage需要在浏览器关闭或是重新打开页面,才会消失;

本地存储可以存储什么?

数组(需要将其序列化为字符串才能存储); json数据――将其转化为字符串存储; 图片 脚本、样式文件:通过ajax

只要能被转化为字符串的数据,都能被localstorage存储;

本地存储如何存储图片

先来看一段代码:

    VAR src="demo.jpg";
      function set(key){
             var img=document.createElement('img');
          img.addEventListener("load",function(){
                 //创建一个canvas             var imgCanvas=document.createElement("canvas"),             imgContext=imgCanvas.getContext("2d");
                 //确保canvas元素的大小和图片的尺寸一致             imgCanvas.width=this.width;
                 imgCanvas.height=this.height;
                 //渲染图片到canvas中,使用canvas的drawImage()方法             imgContext.drawImage(this,0,0,this.width,this.height);
                 //用canvas的dataUrl的形式取出图片,imgAsDataURL是一个base64的字符串             var imgAsDataURL=imgCanvas.toDataURL("image/png");
             //保存到本地存储中             //使用try-catch()查看是否支持localstorage             try{
                     localStorage.setItem(key,imgAsDataURL);
//将取出的图片存放到localStorage              }
             catch(e) {
                  console.LOG("Storage failed:"+e);
//存储失败             }
          }
    ,false);
             img.src=src;
     }
        function get(key) {
    //从本地缓存获取图片并且渲染     var srcStr=localStorage.getItem(key);
    //从localStorage中取出图片     var imgObj=document.createElement('img');
    //创建一个img标签     imgObj.src=srcStr;
         document.body.apPEndChild(imgObj);
     }
              注释:         (1)、这个比较适合用在不常更改的图片,但是如果图片的base64大小比较大的话,将比较耗费localStorage的资源;          (2)、canvas有一个安全策略的问题:如果图片和你本身请求的域名不在同一个域名下,浏览器会报出一个安全问题,这个时候我们要给我们的服务器加一个“允许跨域”访问的响应头――――Access Orign=*,这样来保证你的图片可进行跨域被canvas来画;

HTML5本地存储需要注意的:

使用前判断浏览器是否支持localStorage;(IOS浏览器在无痕模式浏览下,是无法打开localStorage;以及,其他奇葩浏览器,在存储localstorage的时候报错)

做法:根据前面代码,我们在检查是否支持,先进行setItem()一次,然后对setItem进行异常捕获;

写数据的时候,需要异常处理,避免超出容量抛出错误;
localStorage本身只有5M;

避免把敏感的信息存入localStorage;

key的唯一性;重复写,将会覆盖之前的key;

HTML5本地存储使用限制:

存储更新策略,过期控制:localStorage是永不过期的,业务上如果想实现一些过期策略,需要在localStorage上加一层处理过期的机制; 各个子域名之间不能共享存储数据;(借助H5的postMessage()这个API做一些跨域上得处理) 超出存储大小之后如何存储――使用一些如LRU、FIFO的算法去淘汰一些旧的数据; server端如何取到数据――使用post/get参数

处理过期控制

先来看一下代码:

function set(key,y){
             var curTime=new Date().getTime();
         //存储一个当时存储时候的时间         localStorage.setItem(key,JSON.stringify({
data:v,time:curTime}
    ));
      }
     function get(key,exp) {
             var data=localStorage.getItem(key);
             var dataObj=JSON.parse(data);
             if(new Date().getTime()-dataObj.time>
exp) {
    //get出来的时间减去当时存储的时间大于过期时间,那么就认为过期            console.log("过期");
         }
else {
             //否则,返回值          console.log("data="+dataObj.data);
         }
     }
    

本地存储使用场景

本地数据存储,减少网络传输 在弱网络的环境下,会发生高延迟,低带宽,应该尽量把数据(如脚本、样式)本地化;

我们来看一张图,显示的是本地存储和网络拉取耗时的对比:

IndexedDB

概念

IndexedDB,是一种能做浏览器中持久地存储结构化数据的数据库,并且为web应用提供了丰富的查询能力;

支持情况

chrome11+/opera不支持/firefox 4+/IE 10+,移动端浏览器支持能力弱

存储结构

IndexedDB是按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建对个对象存储空间(表/table),一个对象存储空间可以存储多个对象数据;

如图:

使用IndexedDB实现离线数据库

这里我们主要从IndexedDB 的四大功能入手:

增删改 事务处理 游标 索引

下面我们通过一段代码来讲解,请关注里面的注释:

                    姓名:            电话:          地址:                                              script type="text/javascript">
             var db;
             var arrayKey=[];
             var openRequest;
             var lastCursor;
             var indexedDB=window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    //indexedDB在不同的浏览器下不同          var dbName="person";
    //数据库名称         var tableName="testTable";
//表名称         function init() {
                 openRequest=indexedDB.open(dbName);
//页面加载时先打开一个DB,如果该DB存在,则打开;不存在,则新建              //触发事件――当一个“新的数据库”被创建或者数据库的“版本号”被更改时触发             openRequest.onupgradeneeded=function(e){
                     console.log("onupgradeneeded");
                     var thisDb=e.target.result;
                     console.log(thisDb.version);
                  //检查这个数据库中是否包含我们要查找的表                 if(!thisDb.objectStorenames.contains(tableName)){
                         //不包含――创建一个表                     console.log("需要创建一个objectStore");
                     //keyPath:主键,autoIncrement:主键自增                     var objectStore=thisDb.createObjectStore(tableName,{
keyPath:"id",autoIncrement:true}
    );
                     //创建表的时候,指定哪些字段是能被索引的                     objectStore.createIndex("name","name",{
unique:false}
    );
//创建索引                     objectStore.createIndex("phone","phone",{
unique:false}
    );
                 }
              }
             //触发事件――成功打开一个数据库时触发             openRequest.onsuccess=function(e){
                     db=e.target.result;
                     console.log(db.version);
                 db.onerror=function(event){
                         alert("数据库错误:"+event.target.errorCode);
                         console.dir(event.target);
                 }
    ;
                 //判断该数据库中有没有这个表                 if(db.objectStorenames.contains(tableName)){
                         //存在这个表                     console.log("包含表:"+tableName);
                         //通过事物机制操作一个表的读写,从而保证数据的一致性和可靠性                     var transaction=db.transaction([tableName],"readwrite");
                     //事物的事件                     transaction.oncomplete=function(event){
                             console.log("完成");
                     }
    ;
                      transaction.onerror=function(event){
                             console.dir(event);
                     }
    ;
                         var objectStore=transaction.objectStore(tableName);
//通过事物获取表中一个objectStore对象,即表的对象                      //遍历表的记录――游标-openCursor,这是indexedDb的重点                     objectStore.openCursor().onsuccess=function(event){
                             var cursor=event.target.result;
                         if(cursor){
                                 console.log(cursor.key);
                                 console.dir(cursor.value);
                             render({
key:cursor.key,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]}
    );
                                 lastCursor=cursor.key;
    //如果不设置lastCursor,那么游标默认是下一条接着下一条来遍历;设置了lastCursor,游标将循环遍历                             cursor.continue();
                         }
else {
                                 console.log("请使用游标来搞定");
                         }
                     }
    ;
                     objectStore.openCursor().onerror=function(event){
                             console.dir(event);
                     }
    ;
                  }
              }
               //添加新记录             document.querySelector("#add").addEventListener("click",function(){
                     var name=document.querySelector("#name").value();
                     var phone=document.querySelector("#phone").value();
                     var address=document.querySelector("address").value();
                 var person={
"name":name,"phone":phone,"address":address}
    ;
    //设置对象                 //通过事务――操作表                 var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
    //创建一个表对象                 objectStore.add(person);
//添加对象到表中――add()                 //将新增的记录显示处理                 objectStore.openCursor().onsuccess=function(event){
                         cursor=event.target.result;
                         var key;
                     if(lastCursor==null){
                             key=cursor.key;
                             lastCursor=key;
                     }
else {
                             key=++lastCursor;
                     }
                     render({
key:key,name:name,phone:phone,address:address}
    );
                         console.log("成功添加新记录:"+key);
                         console.dir(person);
                 }
              }
    );
              //删除指定ID             function deleteRecord(id){
                     var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
                     var removeKey=parseInt(id);
                     var getRequest=objectStore.get(removeKey);
//获取索引值---get()                 getRequest.onsuccess=function(e){
                         var result=getRequest.result;
                         console.dir(result);
                 }
                     var request=objectStore.delete(removeKey);
//删除――delete                 request.onsuccess=function(e){
                         console.log("删除成功");
                 }
    ;
                 request.onerror=function(e){
                         console.log("删除错误"+e);
                 }
    ;
                     //隐藏删除的DOM                 document.getElementById(removeKey).style.display="none";
             }
              //查询记录             document.querySelector("#seletBtn").addEventListener("click",function(){
                     var curName=document.getElementById("selname").value;                 var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
                     var boundKeyRange=IDBKeyRange.only(curName);
//生成一个表示范围的Range对象---iDBKeyRange,有4个方法,only/lowerBound/upperBound/bound                 objectStore.index("name").openCursor(boundKeyRange).onsuccess=function(event){
                         //从indexedDb中找到name                     var cursor=event.target.result;
                     if(!cursor){
                             return;
                     }
                         var rowData=cursor.value;
                         console.log(rowData);
                         document.getElementById('content').innerHTML="";
                     render({
key:cursor.value.id,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]}
    );
                         cursor.continue();
                 }
    ;
              }
    );
             //删除数据库             document.querySelector("#deleteDB").addEventListener("click",function(){
                     //使用deleteDatabase()                 var deleteDB=indexedDB.deleteDatabase(dbName);
                     var content=document.querySelector("#content");
                 while(content.FirstChild){
                         content.removeChild(content.firstChild);
                 }
                 deleteDB.onsuccess=function(event){
                         console.log("删除成功");
                 }
    ;
                 deleteDB.onerror=function(event){
                         console.dir(event.target);
                 }
    ;
             }
    );
              //渲染             function render(opt){
                     var child_node = document.createElement("p");
                     var child_node_child1 = document.createElement("p");
                     var child_node_child2 = document.createElement("p");
                     var child_node_child3 = document.createElement("p");
                     var child_node_child4 = document.createElement("p");
                     child_node_child1.setattribute("class","table_child");
                     child_node_child2.setAttribute("class","table_child");
                     child_node_child3.setAttribute("class","table_child");
                     child_node_child4.setAttribute("class","table_child");
                     child_node_child1.setAttribute("style","float:left");
                     child_node_child2.setAttribute("style","float:left");
                     child_node_child3.setAttribute("style","float:left");
                     child_node_child4.setAttribute("style","float:left");
                     child_node_child1.innerHTML = name;
                     child_node_child2.innerHTML = opt.phone;
                     child_node_child3.innerHTML = opt.address;
                     child_node_child4.innerHTML = ""                 child_node.appendChild(child_node_child1);
                     child_node.appendChild(child_node_child2);
                     child_node.appendChild(child_node_child3);
                     child_node.appendChild(child_node_child4);
                     child_node.setAttribute("class","table_tr");
                     child_node.setAttribute("id",opt.key);
                     var content = document.getElementById('content');
                 content.appendChild(child_node)              }
          }
         /script>
               

离线缓存――application Cache

何为离线缓存

它是能让web应用在离线的情况下继续使用,通过一个叫manifest的文件指明需要缓存的资源;你可以通过navigator.online检测是否在线;

原理

如图:

解释:

(1)用户通过浏览器(browser)去访问应用,首先检测浏览器是否有一个叫做“App cache”的东西存在,如果存在,则从中检索出app cache所要缓存的list,然后把资源(缓存在浏览器中)拉取出来,返回给用户;

(2)在访问的同时,会检查server上一个叫做manifest的文件,如果该文件有更新,就把manifest指定的文件从server端重新拉取一次,然后把这些缓存在浏览器中,并更新相应的app cache文件;如果manifest这个文件没有更新,那么就啥也不做。

从上图,我们总结2点:

缓存机制的改变,会更新app cache.但是,用户访问,会返回上一次的结果。这样一来,会有一个麻烦,即如果你的业务发生更改,你就需要去更新一次manifest。

注意:更改完,第一次是不生效的,只有第二次刷新才会生效;

如果有一个文件要更新,你就要去更新manifest,而更新manifest文件,它会把server上的文件全部重新拉取一次,而非只是拉取你需要更改的那个文件,这就会造成损耗;

浏览器支持情况

safari on ios 3.2+/andROId 1,5+/window phone 9+

应用

例子:cache.appcache

CACHE MANIFEST      #version 1.0      CACHE:      #需要缓存的文件     /css/a.css     /js/a/js     /images/a.png      NETWORK:      #每次重新拉取的文件      *      FALLBACK      #离线状况下代替的文件      /404.html

在页面上引入manifest文件:

在服务器添加mime-type text/cache-manifest

如果在服务器上添加:

找到你的xampp/apache/conf目录,找到mime.types文件,在最后面添加一条记录:

text/cache-manifest appcache (appcache是后缀名,你可以选择其他的)

我们来看一个例子:

                                        

script type="text/javascript"> window.addEventListener('load',function(e){ //监听app cache的updateready事件 window.applicationCache.addEventListener('updateready',function(e)){ console.log(window.applicationCache.status); if(window.applicatioinCache.status==window.applicationCache.UPDATEREADY){ //application cache的版本号发生改年 window.applicationCache.swapCache(); window.location.reload(); } else { console.log("manifest没有更改"); } } ,false); } ,false); /script>

注意:

app cache会自动地将本页当做一个静态页缓存;

如果你要更新,请更新server端的manifest文件的版本;

如果你不想启用app cache,或者说现在app cache不适合你现在的应用,那么有一个做法:

更改server端上manifest文件的名称,例如cache1.appcache,这个时候再去刷新浏览器,首先,浏览器还是会从app cache缓存中读取缓存,到第二次刷新的时候,浏览器会到server端查找manifest文件,发现这个文件不存在,那么浏览器会走网络从Server上重新拉取文件;

app cache优势:

完全离线 资源缓存,加载更快 降低服务器负载

app cache缺陷:

含有manifest属性的当前请求页无论如何都会被缓存; 更新需要建立在manifest文件的更新,文件更新后是需要页面再次刷新的,并且在第2次刷新才能获取新资源; 更新是全局性的,无法单独更新某个文件; 对于链接的参数变化的敏感的,任何一个参数的修改都会被重新缓存,例如:index.html和index.html?v=1会被认为是不同文件,分别缓存;

app cache适用场景

单地址页面 对实时性要求不要的业务 离线web应用

总结

在实际应用中,我们需要根据业务的需要来采取相应的缓存措施,如上所述,html5的几种缓存都有各自的优缺点和适用场景,有时我们也需要组合使用。

关于HTML5缓存我们就介绍到这里。

参考

HTML5之IndexedDB使用详解

更多前端文章:http://lvtraveler.github.io/

关于存储

说到存储,你可能会想到这是服务器端的一种设置。

服务器端的存储介质大体上分为4种:

cache:缓存,它可以让从数据库、磁盘上输出的东西/数据放置在缓存里,从而减少数据库或是磁盘的读取与写入(IO)操作; 磁盘文件:如,我们常常会将图片、视频等文件存放在磁盘上; 数据库:mySql/mongoDB…关系/非关系数据库; 内存:通常放置频繁要使用到的东西,能够提高读取效率;缓存(cache)也是存放在内存里的;

HTML的存储-cookies

在HTML5出生之前,通常在浏览器(客户端)使用cookies来存储客户端的内容;

cookies的特点:

每次的http请求头中,都会带有cookies――缺点; 每个域名只能存储4K大小的cookies; 主域名污染:如果我们使用cookies存储主域名的东西,那么子域名下得Http请求都会带上主域名的东西;

如果关联上网络,那么将带来安全问题。

所以,通常我们会使用cookies用在如购物车、身份验证等问题上。

下面,我们来看一下百度首页的cookies在浏览器端的一个存储形态:

如图:

HTTP这一列,如果在setCookie的时候,这里就会打钩,这与HTTPOnly相关。vcD4KCjxwPkhUVFBPbmx5o7o8L3A+Cgo8cD7I57n7sNFIVFRQT25secno1sPOqnRydWUsxMfDtGNvb2tpZXPWu8TcsbtzZXJ2ZXK3/s7xxve2y8C0tsHIobvyysfQ3rjEo6y/zbuntsvDu9PQyKjP3r340NC2wcihus3Q3rjEoaPA/cjno6zO0sPH1Nq9+NDQye233dHp1qS1xMqxuvKjrL7Nv8nS1Mq508PV4rj2oaM8L3A+Cgo8cD5TZWN1cmWjutPrsLLIq8/gudijrMjnufvJ6NbDwcujrMTHw7TH68fz1rvE3MrHwLTX1EhUVFC808Pcx+vH86GjPC9wPgoKCgo8aDMgaWQ9"html的存储-userdata"> HTML的存储-UserData 只有IE支持,有微软提供API,但不符合W3C标准; 存储在XML文件中;

HTML5的存储

针对以上问题,HTML5的出现,需要解决以下问题:

解决4K的大小问题; 解决请求头常带存储信息的问题; 解决关系型存储的问题; 跨浏览器平台问题;

HTML5存储形式

本地存储――localstorage /sessionstorage 离线缓存――application cache IndexedDB、Web SQL

本地存储

API:

localstorage 、sessionstorage

存储形式:

keyC> value

过期时间:

localstorage:永久存储,永不失效,除非手动删除
sessionstorage:重新打开页面,或是关闭浏览器,sessionstorage才会消失;

存储大小:
每个域名能存5M;

支持情况:

IE8+,safari3.2+,chrome,firefox等主流浏览器都支持;

使用方法――localstorage/sessionstorage

主要涉及到5个方法:

getItem:获取localstorage/sessionstorage setItem:设置localstorage/sessionstorage removeItem:移除localstorage/sessionstorage key:获取某一个位置上的key值,按值从0开始索引; clear:全部清除localstorage/sessionstorage

例如:我们打开www.baidu.com

在控制台Console输出面板,输入:

localStorage.setItem("test1","test");
那么在Resources面板的Local Storage下,将出现Key=test1,value=test的记录

localStorage.getItem("test1"); //输出test

localStorage.key(0); //输出BDSUGSTOred

sessionstorage的API与localstorage一样,但是你要注意一点:

sessionStorage需要在浏览器关闭或是重新打开页面,才会消失;

本地存储可以存储什么?

数组(需要将其序列化为字符串才能存储); json数据――将其转化为字符串存储; 图片 脚本、样式文件:通过ajax

只要能被转化为字符串的数据,都能被localstorage存储;

本地存储如何存储图片

先来看一段代码:

    var src="demo.jpg";
      function set(key){
             var img=document.createElement('img');
          img.addEventListener("load",function(){
                 //创建一个canvas             var imgCanvas=document.createElement("canvas"),             imgContext=imgCanvas.getContext("2d");
                 //确保canvas元素的大小和图片的尺寸一致             imgCanvas.width=this.width;
                 imgCanvas.height=this.height;
                 //渲染图片到canvas中,使用canvas的drawImage()方法             imgContext.drawImage(this,0,0,this.width,this.height);
                 //用canvas的dataUrl的形式取出图片,imgAsDataURL是一个base64的字符串             var imgAsDataURL=imgCanvas.toDataURL("image/png");
             //保存到本地存储中             //使用try-catch()查看是否支持localstorage             try{
                     localStorage.setItem(key,imgAsDataURL);
//将取出的图片存放到localStorage              }
             catch(e) {
                  console.log("Storage failed:"+e);
//存储失败             }
          }
    ,false);
             img.src=src;
     }
        function get(key) {
    //从本地缓存获取图片并且渲染     var srcStr=localStorage.getItem(key);
    //从localStorage中取出图片     var imgObj=document.createElement('img');
    //创建一个img标签     imgObj.src=srcStr;
         document.body.appendChild(imgObj);
     }
              注释:         (1)、这个比较适合用在不常更改的图片,但是如果图片的base64大小比较大的话,将比较耗费localStorage的资源;          (2)、canvas有一个安全策略的问题:如果图片和你本身请求的域名不在同一个域名下,浏览器会报出一个安全问题,这个时候我们要给我们的服务器加一个“允许跨域”访问的响应头――――Access Orign=*,这样来保证你的图片可进行跨域被canvas来画;

HTML5本地存储需要注意的:

使用前判断浏览器是否支持localStorage;(IOS浏览器在无痕模式浏览下,是无法打开localStorage;以及,其他奇葩浏览器,在存储localstorage的时候报错)

做法:根据前面代码,我们在检查是否支持,先进行setItem()一次,然后对setItem进行异常捕获;

写数据的时候,需要异常处理,避免超出容量抛出错误;
localStorage本身只有5M;

避免把敏感的信息存入localStorage;

key的唯一性;重复写,将会覆盖之前的key;

HTML5本地存储使用限制:

存储更新策略,过期控制:localStorage是永不过期的,业务上如果想实现一些过期策略,需要在localStorage上加一层处理过期的机制; 各个子域名之间不能共享存储数据;(借助H5的postMessage()这个API做一些跨域上得处理) 超出存储大小之后如何存储――使用一些如LRU、FIFO的算法去淘汰一些旧的数据; server端如何取到数据――使用post/get参数

处理过期控制

先来看一下代码:

function set(key,y){
             var curTime=new Date().getTime();
         //存储一个当时存储时候的时间         localStorage.setItem(key,JSON.stringify({
data:v,time:curTime}
    ));
      }
     function get(key,exp) {
             var data=localStorage.getItem(key);
             var dataObj=JSON.parse(data);
             if(new Date().getTime()-dataObj.time>
exp) {
    //get出来的时间减去当时存储的时间大于过期时间,那么就认为过期            console.log("过期");
         }
else {
             //否则,返回值          console.log("data="+dataObj.data);
         }
     }
    

本地存储使用场景

本地数据存储,减少网络传输 在弱网络的环境下,会发生高延迟,低带宽,应该尽量把数据(如脚本、样式)本地化;

我们来看一张图,显示的是本地存储和网络拉取耗时的对比:

IndexedDB

概念

IndexedDB,是一种能做浏览器中持久地存储结构化数据的数据库,并且为web应用提供了丰富的查询能力;

支持情况

chrome11+/opera不支持/firefox 4+/IE 10+,移动端浏览器支持能力弱

存储结构

IndexedDB是按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建对个对象存储空间(表/table),一个对象存储空间可以存储多个对象数据;

如图:

使用IndexedDB实现离线数据库

这里我们主要从IndexedDB 的四大功能入手:

增删改 事务处理 游标 索引

下面我们通过一段代码来讲解,请关注里面的注释:

                    姓名:            电话:          地址:                                              script type="text/javascript">
             var db;
             var arrayKey=[];
             var openRequest;
             var lastCursor;
             var indexedDB=window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    //indexedDB在不同的浏览器下不同          var dbName="person";
    //数据库名称         var tableName="testTable";
//表名称         function init() {
                 openRequest=indexedDB.open(dbName);
//页面加载时先打开一个DB,如果该DB存在,则打开;不存在,则新建              //触发事件――当一个“新的数据库”被创建或者数据库的“版本号”被更改时触发             openRequest.onupgradeneeded=function(e){
                     console.log("onupgradeneeded");
                     var thisDb=e.target.result;
                     console.log(thisDb.version);
                  //检查这个数据库中是否包含我们要查找的表                 if(!thisDb.objectStoreNames.contains(tableName)){
                         //不包含――创建一个表                     console.log("需要创建一个objectStore");
                     //keyPath:主键,autoIncrement:主键自增                     var objectStore=thisDb.createObjectStore(tableName,{
keyPath:"id",autoIncrement:true}
    );
                     //创建表的时候,指定哪些字段是能被索引的                     objectStore.createIndex("name","name",{
unique:false}
    );
//创建索引                     objectStore.createIndex("phone","phone",{
unique:false}
    );
                 }
              }
             //触发事件――成功打开一个数据库时触发             openRequest.onsuccess=function(e){
                     db=e.target.result;
                     console.log(db.version);
                 db.onerror=function(event){
                         alert("数据库错误:"+event.target.errorCode);
                         console.dir(event.target);
                 }
    ;
                 //判断该数据库中有没有这个表                 if(db.objectStoreNames.contains(tableName)){
                         //存在这个表                     console.log("包含表:"+tableName);
                         //通过事物机制操作一个表的读写,从而保证数据的一致性和可靠性                     var transaction=db.transaction([tableName],"readwrite");
                     //事物的事件                     transaction.oncomplete=function(event){
                             console.log("完成");
                     }
    ;
                      transaction.onerror=function(event){
                             console.dir(event);
                     }
    ;
                         var objectStore=transaction.objectStore(tableName);
//通过事物获取表中一个objectStore对象,即表的对象                      //遍历表的记录――游标-openCursor,这是indexedDb的重点                     objectStore.openCursor().onsuccess=function(event){
                             var cursor=event.target.result;
                         if(cursor){
                                 console.log(cursor.key);
                                 console.dir(cursor.value);
                             render({
key:cursor.key,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]}
    );
                                 lastCursor=cursor.key;
    //如果不设置lastCursor,那么游标默认是下一条接着下一条来遍历;设置了lastCursor,游标将循环遍历                             cursor.continue();
                         }
else {
                                 console.log("请使用游标来搞定");
                         }
                     }
    ;
                     objectStore.openCursor().onerror=function(event){
                             console.dir(event);
                     }
    ;
                  }
              }
               //添加新记录             document.querySelector("#add").addEventListener("click",function(){
                     var name=document.querySelector("#name").value();
                     var phone=document.querySelector("#phone").value();
                     var address=document.querySelector("address").value();
                 var person={
"name":name,"phone":phone,"address":address}
    ;
    //设置对象                 //通过事务――操作表                 var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
    //创建一个表对象                 objectStore.add(person);
//添加对象到表中――add()                 //将新增的记录显示处理                 objectStore.openCursor().onsuccess=function(event){
                         cursor=event.target.result;
                         var key;
                     if(lastCursor==null){
                             key=cursor.key;
                             lastCursor=key;
                     }
else {
                             key=++lastCursor;
                     }
                     render({
key:key,name:name,phone:phone,address:address}
    );
                         console.log("成功添加新记录:"+key);
                         console.dir(person);
                 }
              }
    );
              //删除指定ID             function deleteRecord(id){
                     var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
                     var removeKey=parseInt(id);
                     var getRequest=objectStore.get(removeKey);
//获取索引值---get()                 getRequest.onsuccess=function(e){
                         var result=getRequest.result;
                         console.dir(result);
                 }
                     var request=objectStore.delete(removeKey);
//删除――delete                 request.onsuccess=function(e){
                         console.log("删除成功");
                 }
    ;
                 request.onerror=function(e){
                         console.log("删除错误"+e);
                 }
    ;
                     //隐藏删除的DOM                 document.getElementById(removeKey).style.display="none";
             }
              //查询记录             document.querySelector("#seletBtn").addEventListener("click",function(){
                     var curName=document.getElementById("selname").value;                 var transaction=db.transaction([tableName],"readwrite");
                 transaction.oncomplete=function(event){
                         console.log("事务处理完成");
                 }
    ;
                 transaction.onerror=function(event){
                         console.dir(event);
                 }
    ;
                     var objectStore=transaction.objectStore(tableName);
                     var boundKeyRange=IDBKeyRange.only(curName);
//生成一个表示范围的Range对象---IDBKeyRange,有4个方法,only/lowerBound/upperBound/bound                 objectStore.index("name").openCursor(boundKeyRange).onsuccess=function(event){
                         //从indexedDb中找到name                     var cursor=event.target.result;
                     if(!cursor){
                             return;
                     }
                         var rowData=cursor.value;
                         console.log(rowData);
                         document.getElementById('content').innerHTML="";
                     render({
key:cursor.value.id,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]}
    );
                         cursor.continue();
                 }
    ;
              }
    );
             //删除数据库             document.querySelector("#deleteDB").addEventListener("click",function(){
                     //使用deleteDatabase()                 var deleteDB=indexedDB.deleteDatabase(dbName);
                     var content=document.querySelector("#content");
                 while(content.firstChild){
                         content.removeChild(content.firstChild);
                 }
                 deleteDB.onsuccess=function(event){
                         console.log("删除成功");
                 }
    ;
                 deleteDB.onerror=function(event){
                         console.dir(event.target);
                 }
    ;
             }
    );
              //渲染             function render(opt){
                     var child_node = document.createElement("p");
                     var child_node_child1 = document.createElement("p");
                     var child_node_child2 = document.createElement("p");
                     var child_node_child3 = document.createElement("p");
                     var child_node_child4 = document.createElement("p");
                     child_node_child1.setAttribute("class","table_child");
                     child_node_child2.setAttribute("class","table_child");
                     child_node_child3.setAttribute("class","table_child");
                     child_node_child4.setAttribute("class","table_child");
                     child_node_child1.setAttribute("style","float:left");
                     child_node_child2.setAttribute("style","float:left");
                     child_node_child3.setAttribute("style","float:left");
                     child_node_child4.setAttribute("style","float:left");
                     child_node_child1.innerHTML = name;
                     child_node_child2.innerHTML = opt.phone;
                     child_node_child3.innerHTML = opt.address;
                     child_node_child4.innerHTML = ""                 child_node.appendChild(child_node_child1);
                     child_node.appendChild(child_node_child2);
                     child_node.appendChild(child_node_child3);
                     child_node.appendChild(child_node_child4);
                     child_node.setAttribute("class","table_tr");
                     child_node.setAttribute("id",opt.key);
                     var content = document.getElementById('content');
                 content.appendChild(child_node)              }
          }
         /script>
               

离线缓存――application Cache

何为离线缓存

它是能让web应用在离线的情况下继续使用,通过一个叫manifest的文件指明需要缓存的资源;你可以通过navigator.online检测是否在线;

原理

如图:

解释:

(1)用户通过浏览器(browser)去访问应用,首先检测浏览器是否有一个叫做“App cache”的东西存在,如果存在,则从中检索出app cache所要缓存的list,然后把资源(缓存在浏览器中)拉取出来,返回给用户;

(2)在访问的同时,会检查server上一个叫做manifest的文件,如果该文件有更新,就把manifest指定的文件从server端重新拉取一次,然后把这些缓存在浏览器中,并更新相应的app cache文件;如果manifest这个文件没有更新,那么就啥也不做。

从上图,我们总结2点:

缓存机制的改变,会更新app cache.但是,用户访问,会返回上一次的结果。这样一来,会有一个麻烦,即如果你的业务发生更改,你就需要去更新一次manifest。

注意:更改完,第一次是不生效的,只有第二次刷新才会生效;

如果有一个文件要更新,你就要去更新manifest,而更新manifest文件,它会把server上的文件全部重新拉取一次,而非只是拉取你需要更改的那个文件,这就会造成损耗;

浏览器支持情况

safari on ios 3.2+/android 1,5+/window phone 9+

应用

例子:cache.appcache

CACHE MANIFEST      #version 1.0      CACHE:      #需要缓存的文件     /css/a.css     /js/a/js     /images/a.png      NETWORK:      #每次重新拉取的文件      *      FALLBACK      #离线状况下代替的文件      /404.html

在页面上引入manifest文件:

在服务器添加mime-type text/cache-manifest

如果在服务器上添加:

找到你的xampp/apache/conf目录,找到mime.types文件,在最后面添加一条记录:

text/cache-manifest appcache (appcache是后缀名,你可以选择其他的)

我们来看一个例子:

                                        

script type="text/javascript"> window.addEventListener('load',function(e){ //监听app cache的updateready事件 window.applicationCache.addEventListener('updateready',function(e)){ console.log(window.applicationCache.status); if(window.applicatioinCache.status==window.applicationCache.UPDATEREADY){ //application cache的版本号发生改年 window.applicationCache.swapCache(); window.location.reload(); } else { console.log("manifest没有更改"); } } ,false); } ,false); /script>

注意:

app cache会自动地将本页当做一个静态页缓存;

如果你要更新,请更新server端的manifest文件的版本;

如果你不想启用app cache,或者说现在app cache不适合你现在的应用,那么有一个做法:

更改server端上manifest文件的名称,例如cache1.appcache,这个时候再去刷新浏览器,首先,浏览器还是会从app cache缓存中读取缓存,到第二次刷新的时候,浏览器会到server端查找manifest文件,发现这个文件不存在,那么浏览器会走网络从Server上重新拉取文件;

app cache优势:

完全离线 资源缓存,加载更快 降低服务器负载

app cache缺陷:

含有manifest属性的当前请求页无论如何都会被缓存; 更新需要建立在manifest文件的更新,文件更新后是需要页面再次刷新的,并且在第2次刷新才能获取新资源; 更新是全局性的,无法单独更新某个文件; 对于链接的参数变化的敏感的,任何一个参数的修改都会被重新缓存,例如:index.html和index.html?v=1会被认为是不同文件,分别缓存;

app cache适用场景

单地址页面 对实时性要求不要的业务 离线web应用

总结

在实际应用中,我们需要根据业务的需要来采取相应的缓存措施,如上所述,html5的几种缓存都有各自的优缺点和适用场景,有时我们也需要组合使用。

关于HTML5缓存我们就介绍到这里。

参考

HTML5之IndexedDB使用详解

觉得可用,就经常来吧! 欢迎评论哦! html5教程,巧夺天工,精雕玉琢。小宝典献丑了!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

AJAXAPIArrayClassCSSdivDOMHTMLhtml5letpost-format-gallerythis数组

若转载请注明出处: 【缓存/性能】HTML5缓存的那些事
本文地址: https://pptw.com/jishu/587069.html
2016年 最火的 15 款 HTML5 游戏引擎 一个有趣的html5手机锁分析

游客 回复需填写必要信息