初探HTML5的本地存储

传统的HTML使用的是众所周知的cookie,各种浏览器都支持,直接用js就可以调用,很方便。但cookie也有它本身的缺陷与不足。比如存储空间小,每个站点大小限制在4 KB左右,又有时间期限…

 传统的HTML使用的是众所周知的cookie,各种浏览器都支持,直接用js就可以调用,很方便。但cookie也有它本身的缺陷与不足。比如存储空间小,每个站点大小限制在4 KB左右,又有时间期限,而且在请求网页的时候cookie会被附加在每个HTTP请求的header中,所以无形中增加了流量,在HTTP请求中的cookie是明文传递的,所以安全性成问题,当然你可以用SSL通道,这另当别论。cookie还很容易受到跨站脚本的攻击,在一个链接地址后面加上“?cookie=document.cookie”就可以很容易地获得用户的cookie信息。当然, HTML5的本地存储可能也会有跨站脚本攻击XSS的问题,这个不是本文讨论的主要内容,以后我们一起慢慢研究。

  长久以来本地存储能力一直是桌面应用有别于Web应用的一个主要优势,前者可以自由的操作本地文件系统,存储和读取都很方便,比如ini、xml等本地配置文件,存于本地,不需要在每次用的时候都要去服务器上取配置数据。web应用就不同,为了安全,浏览器都不会让网页脚本去调用本地的文件系统,除了cookie,自然也不能存储或使用本地配置文件,用户的个性化设置一般都是存在浏览器上。再比如,用户在网页上编辑一篇很长的文章,编辑的过程中网页要自动帮用户保存。存哪?本地cookie很可能不够用,空间太小,而且还有时候期限,所以只能保存到服务器上去。

  HTML5的新标准可以很好地解决上面的一些问题。当然,本地存储还可以解决更多我暂时没提到的问题,留给读者自己去探索。本地存储可以存储5M大小的数据,甚至还可以更多。它主要有四种:localstorage , sessionstorage, webSQL , indexedDB。

1、localstorage

  用window.localStorage,但浏览器可能不支持,它就有可能是空,所以我们先做个判断。

JavaScript Code 复制内容到剪贴板
  1. functiongetLocalStorage(){
  2. try{
  3. if(!!window.localStorage)returnwindow.localStorage;
  4. }catch(e){
  5. returnundefined;
  6. }
  7. }

(两个感叹号用于判断变量是否为null、undefined、''、0,个人理解,这是书写习惯的问题,把它去掉也没错。这里是为了规范if()里面一般是放bool类型的,而js比较灵活,上面提到的那四种语法上当作false来处理,在它们前面加上一个感叹号,则变成真正的bool类型,但是true,所以再加个感叹号,变回false,不是上面四种类型的都变true)

存数据:

JavaScript Code 复制内容到剪贴板
  1. vardb=getLocalStorage();
  2. 2if(db){
  3. 3db.setItem('author','jasonling');
  4. 4db.setItem('company','Tencent');
  5. 5db.setItem('introduction','Acodelover!');
  6. 6}

  其实,localStorage就是键值对,它的值可以存大小多达5M的字符串。事实上,它是存到一个sqlite的文件中去,用sqlite打开可以看到里面自己建了一个表,里面有我们刚刚存的数据。

  chrome浏览器的这个sqlite文件存在:C:/Users/你的用户名/AppData/Local/Google/Chrome/User Data/Default/Local Storage 里面,如上图,用sqlite打开,用.schema可以看到一个表,这是chrome帮我们建的用来存储键值对的表ItemTable,查询表里的数据可以发现我们刚刚用程序存进去的值。

其它浏览器中,sqlite文件的存储位置,读者可以自己去探索探索。

localstorage的其它操作:

JavaScript Code 复制内容到剪贴板
  1. db.setItem('author','jasonling');
  2. 2db.setItem('company','Tencent');
  3. 3db.setItem('introduction','Acodelover!');
  4. 4//取值
  5. 5alert(localStorage['author']);
  6. 6alert(db.getItem('company'));
  7. 7//删除
  8. 8db.removeItem('company');
  9. 9alert(db.getItem('company'));
  10. 10//当然也可以用db.setItem('company','');来删除一个值,但这样删不彻底。
  11. 11//清除
  12. 12db.clear();
  13. 13alert(localStorage['author']);//undefined

在浏览器上同时打开两个页面(Lynda.com上的一个示例),如图:

  这两个页面都是一样的,当我用在一个页面中进行修改,另一个页面的数据会不会自动修改呢?

显然不会,为了达到自动更新的效果,我们得用事件监听器。

一样的道理,先要判断浏览器支不支持:

JavaScript Code 复制内容到剪贴板
  1. //GettheaddEventListener
  2. functiongetAddEventHandler(){
  3. try{
  4. if(!!window.addEventListener)returnwindow.addEventListener;
  5. }catch(e){
  6. returnundefined;
  7. }
  8. }

写一个handler函数.

JavaScript Code 复制内容到剪贴板
  1. //EventStatus
  2. functioneventStatus(s){
  3. if(s)element('eventResult').innerHTML=s;
  4. elseelement('eventResult').innerHTML='Eventstatus';
  5. }
  6. //Eventhandler
  7. functioneventHandler(e){
  8. eventStatus('Eventtriggered:'+e.url+''+
  9. e.storageArea.traveler+''+e.storageArea.destination+''+e.storageArea.transportation);
  10. dispResults();
  11. }

eventStatus用来作判断,当字符串s有问题时,就是else,当它没问题时就if。为了进一步说明,我写了下面一个例子:

JavaScript Code 复制内容到剪贴板
  1. functiontest(s){
  2. if(a){
  3. alert('if');
  4. }else{
  5. alert('else');
  6. }
  7. }
  8. vara=window.abc;//window.abc不存在,是有问题的,所以,可以想像,下面的结果是else
  9. test(a);

  这个其实是js的一个技巧,有问题的字符串,在if里面当false处理。这要和未定义的变量放if里面区别开来,如果一个变量未定义,那么不管放哪,js都会停止执行。如,如果上面没有定义b,即没有var b; 这句,下面突然出现了个if(b)那么程序到这里就会中断。大部分浏览器是这样的。 回到正题上来:

JavaScript Code 复制内容到剪贴板
  1. varaddEL=getAddEventHandler();//这是上面写的一个函数
  2. if(addEL){
  3. addEL('storage',eventHandler,false);//给storage事件加上监听器
  4. }else{
  5. element('eventResult').innerHTML='Thisbrowserdoesnotsupporteventlisteners';
  6. }
  7. dispResults();//这是自定义的一个函数,读者不用管,作用是显示结果

  这样,每次local storage里面的数据变化时,浏览器里面的数据就会自动变化了,因为storage事件被监听后,浏览器就会时时监视local storage里的数据,如果变化,就会触发事件,修改页面。不管几个页面都会做出相应的修改。

要注意一点,这个事件只有在同一个浏览器程序里面才有效(比如你不能在chrome和firefox各打开一个页面,然后等着事件生效),因为不同浏览器的sqlite文件不一样,各自修改自己的数据,当然不会对其它浏览器的数据造成影响。

经过上面的介绍,大家可以看出,忽略事件的内容外,localstorage的操作其实也很简单,和cookie一样很容易操作。下面介绍的几个会一个比一个难,这里先做好心里准备。

2、sessionStorage

  localStorage的数据是页面共享的,但有些情况,我们需要一个浏览器中的不同页面可以单独操作自己的数据。这时我们就可以用sessionStorage了,它存储的数据只有当前页面可以访问。

第一步还是一样,判断浏览器支不支持:

JavaScript Code 复制内容到剪贴板
  1. functiongetSessionStorage(){
  2. try{
  3. if(!!window.sessionStorage)returnwindow.sessionStorage;
  4. }catch(e){
  5. returnundefined;
  6. }
  7. }

然后:

JavaScript Code 复制内容到剪贴板
  1. vardb=getSessionStorage();
  2. if(db){
  3. db.setItem('author','jasonling');
  4. db.setItem('company','Tencent');
  5. db.setItem('introduction','Acodelover!');
  6. }
  7. //和localStorage一样的操作

  然后打开浏览器,同时打开两个网页做测试,我们可以看到,这两个页面中的数据不会相互影响。还是用Lynda.com的一个例子来展示:

  再怎么刷新这两个页面,他们之间的数据还是不会相互影响。

  那么,sessionStorage把数据存哪了?经过测试发现,它保存在

C:\Users\你的用户名\AppData\Local\Google\Chrome\User Data\Default\Current Session 这个文件里面。但它不是一个sqlite文件,因为用sqlite打不开。具体怎么存的,作者本人也没研究出来,如有高手知道,请留言告之。

3、webSQL

  其实就是sqlite数据库,在js中可以像java在本地调用mysql数据库一样的方便,可以自己创建数据库,自己建表,自己往里面增删改数据。

  第一步还是要判断一下浏览器是否支持:

Javascript 复制内容到剪贴板
  1. functiongetOpenDatabase(){
  2. try{
  3. if(!!window.openDatabase)returnwindow.openDatabase;
  4. elsereturnundefined;
  5. }catch(e){
  6. returnundefined;
  7. }
  8. }

如果支持,就试着打开数据库连接:

JavaScript Code 复制内容到剪贴板
  1. functionprepareDatabase(){
  2. varodb=getOpenDatabase();
  3. if(!odb){
  4. dispError('WebSQLNotSupported');
  5. returnundefined;
  6. }else{
  7. vardb=odb('testDatabase','1.0','ATestDatabase',10*1024*1024);
  8. db.transaction(function(t){
  9. t.executeSql(createSQL,[],function(t,r){},function(t,e){
  10. alert('createtable:'+e.message);
  11. });
  12. });
  13. returndb;
  14. }
  15. }

  其中,odb('testDatabase', '1.0','A Test Database',10*1024*1024);试着打开(新建)一个数据库数据库,四个参数分别表示:数据库名,数据库版本,数据库描述,和它预定的大小。

   db.transaction那一句,是WebSQL中最常用的语法。createSQL是一个用来创建数据库的字符串:

var createSQL = 'CREATE TABLE IF NOT EXISTS tTravel (' + 'id INTEGER PRIMARY KEY,' + 'traveler TEXT,' + 'destination TEXT,' + 'transportation TEXT' + ')';

  transaction这个函数,我在网上找了很多资料,都说是后面只能带一个参数,但我测试之后,发现是可以带三个参数,第二个是错误处理函数,两参(t,e),分别表示transaction和error,第三个成功回调函数,无参。。重点是第一个参数,是一个方程,四个参数:

function(t) {

  t.executeSql("要执行的sql语句_需要参数的地主用?代替", [参数_用逗号隔开], function(t,r){成功回调函数_t表示transaction_r是result}, function(t,e){出错回调函数_t表示transaction_e是error});

}

比如用用户名ling和密码mypwd来登陆,可以这样写:

JavaScript Code 复制内容到剪贴板
  1. function(t){
  2. t.executeSql('select*fromtUserwherename=?andpwd=?',['ling','mypwd'],
  3. function(t,r){
  4. alert('登陆成功');
  5. },
  6. function(t,e){
  7. alert(e.message);
  8. }
  9. }

很方便,完全可以使用sql语句来操作。

  回到原来的话题,var db = prepareDatabase(); 来得到我们在数据库中建的表tTravel。接下去我们就可以用它来操作这个表。下面举一些例子:(其中有些这是Lynda.com提供的一些例子,bwTable是它自己实现的一个漂亮的表格,用来显示数据,读者不用深究这个,我会把最后的代码一起传上来)

(1)计算表里面有几行记录:

JavaScript Code 复制内容到剪贴板
  1. //Getthecountofrows
  2. functioncountRows(){
  3. if(!db)return;
  4. db.readTransaction(function(t){
  5. t.executeSql('SELECTcount(*)AScFROMtTravel',[],function(t,r){
  6. varc=r.rows.item(0).c;//result结果的第行的c那一列
  7. element('rowCount').innerHTML=c?c:0;
  8. },function(t,e){
  9. alert('countRows:'+e.message);
  10. });
  11. });
  12. }

  语法和上面提到的一样,只是这边用的是readTransaction,这是为了保证不对表进行写操作,这是一种安全的举措,当然也可以用transaction。

(2)取出表中的数据:

JavaScript Code 复制内容到剪贴板
  1. if(db){
  2. db.readTransaction(function(t){
  3. t.executeSql('SELECT*FROMtTravelORDERBYLOWER(traveler)',[],function(t,r){
  4. varmytab=newbwTable();
  5. mytab.setHeader(['Traveler','Destination','Transportation','']);
  6. for(vari=0;i<r.rows.length;i++){
  7. varrow=r.rows.item(i);
  8. mytab.addRow([row.traveler,row.destination,row.transportation,rowBtn(row.id,row.traveler)]);
  9. }
  10. element('results').innerHTML=mytab.getTableHTML();
  11. element("travelForm").elements['traveler'].focus();
  12. },function(t,e){
  13. alert("Can'tgetthedata");
  14. });});
  15. }

(3)往表里添加数据:

JavaScript Code 复制内容到剪贴板
  1. if(db){
  2. db.transaction(function(t){
  3. t.executeSql('INSERTINTOtTravelVALUES(NULL,?,?,?)',
  4. [traveler,destination,transportation],
  5. function(t,r){resetForm();},//执行成功时把表格清空,自己实现的一个函数
  6. function(t,e){
  7. alert("insertrows:"+e.message);
  8. });
  9. });
  10. }

(4)更新数据:

JavaScript Code 复制内容到剪贴板
  1. db.transaction(function(t){
  2. t.executeSql('updatetTravelsettraveler=?,destination=?,transportation=?whereid=?',
  3. [traveler,destination,transportation,inputKey]);
  4. },function(t,e){
  5. alert('Updaterow:'+e.message);
  6. },function(){resetForm();});

(5)删除数据:

JavaScript Code 复制内容到剪贴板
  1. if(db){
  2. db.transaction(function(t){
  3. t.executeSql('deletefromtTravelwhereid=?',[id],
  4. function(t,r){alert('SUCCESSFULLY!');});
  5. });
  6. }

  其实很容易,就是简单地用sql来操作数据库。

  chrome的webSQL本地数据存在这个目录下:C:\Users\你的用户名\AppData\Local\Google\Chrome\User Data\Default\databases文件夹,里面一般会有file__0等类似的文件夹,可以用sqlite打开来看看。

4、indexedDB

  有一篇文章《Indexed DB:未来一切 Web 应用的基石》在总体上介绍了一下indexedDB,读者可以去网搜一搜,读一读,大概了解一下它的前世今生!我摘录了《HTML5之IndexedDB使用详解》里面的一些内容来简单的介绍一下:它是HTML5-WebStorage的重要一环,是一种轻量级NOSQL数据库,w3c为IndexedDB定义了很多接口,其中Database对象被定义为IDBDataBase。而得到IDBDataBase用的是工厂方法,即从IDBFactory中取得。浏览器对象中,实现了IDBFactory的只有indexedDB这个实例。IndexedDB中,几乎所有的操作都是采用了command->request->result的方式。比如查询一条记录,返回一个request,在request的result中得到查询结果。又比如打开数据库,返回一个request,在request的result中得到返回的数据库引用。(摘录结束)

  indexedDB的使用比前三种复杂了些,下面我们一部分一部分来了解。

  首先要注意的一点是,indexedDB的页面只有放到服务器上才能正常访问,单独双击页面很可能不会成功。但即使这样,indexedDB的数据还是存在用户本地的。目前测试了一下,chrome上delete操作有点问题,不过读者可以在firefox上测试,可以正常跑通。

  第一步也是判断并获得indexedDB对象:

JavaScript Code 复制内容到剪贴板
  1. functiongetIndexDB(){
  2. try{
  3. //由于浏览器不同内核中,indexedDB的对象名不同,所以比较麻烦:
  4. if(!window.indexedDB)window.indexedDB=window.mozIndexedDB||window.webkitIndexedDB;
  5. //webkitIndexedDB内核的浏览器中,IDBTransaction的名字有些差异
  6. if('webkitIndexedDB'inwindow)window.IDBTransaction=window.webkitIDBTransaction;
  7. if(!!window.indexedDB){
  8. returnwindow.indexedDB;
  9. }else{
  10. returnundefined;
  11. }
  12. }catch(e){
  13. returnundefined;
  14. }
  15. }

  然后打开indexedDB,比较复杂:

JavaScript Code 复制内容到剪贴板
  1. functionopenDB(){
  2. variDB=getIndexDB();
  3. if(!iDB){
  4. dispError('IndexDBnotsupported!');
  5. return;
  6. }else{
  7. try{
  8. //打开或创建一个indexedDB,名travelDB,后面那参数是描述。
  9. varrequest=iDB.open('travelDB','demotravelDB');
  10. request.οnerrοr=function(event){//request.onerror是打开失败时的处理函数
  11. dispError('FailedtoopenIndexDBdatabase');
  12. }
  13. request.onsuccess=function(event){//request.onerror是打开成功时的回调函数
  14. db=request.result;
  15. //不要以为到这里很复杂,其实到上一步就是:
  16. //window.indexedDB.open('travelDB','demotravelDB').result
  17. //只是我们在一步步的做安全处理,下面是request在获取结果失败时的处理
  18. db.οnerrοr=function(event){dispError('Databaseerror:'+event.target.errorCode);}
  19. //indexedDB比较麻烦的一点是还要设置版本,如果不版本不统一,
  20. //在之后某些步骤调用时,会有问题。一般都把它设成1.0
  21. if(db.version!='1.0'){
  22. varreq=db.setVersion('1.0');//Settheversion
  23. //设置了version之后还要进行错误处理
  24. req.οnerrοr=function(event){alert('versionerror!');}
  25. req.onsuccess=function(event){
  26. //alert('Creatingtheobjectstore');
  27. //创建一个ObjectStore,id是主键,自动增长
  28. varobjStore=db.createObjectStore('oTravel',
  29. {keyPath:'id',autoIncrement:true});
  30. //创建一个index:traveler,不唯一
  31. objStore.createIndex('traveler','ciTraveler',{unique:false});
  32. }
  33. }
  34. }
  35. }catch(e){
  36. dispError('IndexDBsupported,butcan\'topenthedatabase\n'+e.message);
  37. }
  38. }
  39. }

  其中db是在函数外定义的一个变量:var db;这样就可以在全局使用。

  下面再举几个查询、插入、修改、删除的例子:

(1)查询

JavaScript Code 复制内容到剪贴板
  1. if(db){
  2. //先得到我们刚刚创建的objectStore,transaction里面的可以有两个参数,
  3. //第一个参数为要关联的数据库名称数组,第二个为打开此数据库的方式(如只读),
  4. //下面会讲,它缺省时表示打开的方式为只读
  5. //然后由transaction得到名为oTravel的objectStore
  6. varobjStore=db.transaction(['oTravel']).objectStore('oTravel');
  7. //可以用objStore来打开游标,也可以用index来打开游标,它们的区别下面会讲
  8. //varindexTraveler=objStore.index('traveler');
  9. objStore.openCursor().onsuccess=function(event){//openthecursoroftheindex
  10. varcursor=event.target.result;//由event来获得游标
  11. if(cursor){
  12. varv=cursor.value;
  13. alert(v.traveler+'乘坐'+v.transportation+'去'+v.destination);
  14. cursor.continue();
  15. //continue其实就是让游标往下移,并试着open,成功时还是执行这个函数,
  16. //所以这个其实是一个隐藏的循环,直到cursor读完为止
  17. }
  18. }
  19. }

transaction的第二个参数表示打开的方式,主要有以下几种:

IDBTransaction.READ_ONLY 只读

IDBTransaction.READ_WRITE 可读可写

IDBTransaction.VERSION_CHANGE 版本升级

用的最多的是前两种。不设置时则默认为READ_ONLY  

(2)插入

  由上面例子可以体验到一些nosql数据库的优点,直接对对象进行操作,取出的是对象,写入的也是对象,如下:

JavaScript Code 复制内容到剪贴板
  1. curRec={traveler:traveler,destination:destination,transportation:transportation,ciTraveler:traveler.toLowerCase()};
  2. db.transaction(['oTravel'],IDBTransaction.READ_WRITE).objectStore('oTravel').add(curRec);

  很简单,就是把对象插入数据库中。

(3)修改

其实就是先把原来相应的那条数据给删除,然后加入这条修改后的数据

JavaScript Code 复制内容到剪贴板
  1. curRec={traveler:traveler,destination:destination,transportation:transportation,ciTraveler:traveler.toLowerCase()};
  2. varobjStore=db.transaction('oTravel',IDBTransaction.READ_WRITE).objectStore('oTravel');
  3. //alert(key);
  4. varrequest;
  5. if(objStore.delete){
  6. request=objStore.delete(key);
  7. }else{
  8. //FF4notuptospect
  9. request=objStore.remove(key);
  10. }
  11. request.οnerrοr=function(e){
  12. dispError('RemoveError!');
  13. }
  14. request.onsuccess=function(e){
  15. objStore.add(curRec);
  16. };

(4)删除

  删除时要先得到id,把id传入数据库引擎就知道删除哪一条数据了,步骤和上面差不多。

JavaScript Code 复制内容到剪贴板
  1. if(confirm('Areyousuretodeletetraveler:'+traveler)){
  2. varobjStore=db.transaction('oTravel',IDBTransaction.READ_WRITE).objectStore('oTravel');
  3. //alert(id);
  4. varrequest;
  5. if(objStore.delete){
  6. request=objStore.delete(id);
  7. }else{
  8. //FF4notuptospect
  9. request=objStore.remove(id);
  10. }
  11. request.οnerrοr=function(e){
  12. dispError('RemoveError!');
  13. }
  14. }

 

  上面讲到的,用objStore来打开游标,也可以用index来打开游标。有一篇文章讲得很清楚,我把它摘过来供读者参考。

数据本地化存储》这文章比较长,可以搜 openCursor 来找到你想要的内容

  chrome的indexedDB本地数据存在:C:\Users\你的用户名\AppData\Local\Google\Chrome\User Data\Default\IndexedDB这个目录下,它会根据站点的名字来命名,其实上面三种也是用站点名字来命名,只是在本地用双击的方式访问时,会写成file之类的名字。

  如果读者对indexedDB还是不太了解的话,可以参考上面提到的这篇文章:《HTML5之IndexedDB使用详解》,它写得比较详细。

(最后,我会把本文中所讲到的示例文件都上传到我的博客中了,可以下载运行)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值