下面开始,走你。
Web App的本地存储的过去,现在和未来
目录
- 潜深一点吧
- HTML5史前时期的本地存储
- HTML5存储那点事儿
- 使用HTML5存储
- 追踪HTML5存储区发生的改变
- 现代浏览器那些得瑟的龟毛
- 用HTML5存储真的干一场吧
- 除了键值对还能怎么搞
- 一些福利
Diving In
长期以来,永久性的本地存储是native app秒杀web app的一大利器。对于native app,OS常常会提供一个抽象层专门为其提供应用相关的数据请求的存取。这些值被存在诸如寄存器、INI文件、XML文件或者依赖于平台惯例的定义的其他地方。而且如果你的native app的数据处理需求远远超过只由key/value提供的基本功能,那么OS甚至允许你自己嵌入DB,使用你自定义的文件类型,或者其他的解决方案。
历史以来,web app就像是后娘养的,从来就没有这些福利待遇,只能眼巴巴地可怜兮兮地用着cookie而已。但是你懂的,这玩意只能用于小量的数据存储,还你Y的会expire呀,而现在这种SPA的大时代下,显然是不够塞牙缝的。下面来看看cookie的几个违反规矩的缺点(dealbreaking downsides)吧。
- 在每个http请求过程中,cookie总是会被携带着,真是个累赘,增加了web app响应的速度,有时候就算明明没有任何更新,也还是得带上它,完全就是在花无用功在宝贵的带宽中来来回回地传输没有意义的相同的数据。
- 如上所述,每个http请求来回过程中,cookie其实是没有经过编码的,除非你的整个web app是基于SSL支持的,否则安全性存在问题。
- 最致命的弱点,简直是硬伤呀,4KB的数据,够干嘛的呀,传一张私密图片,木有了~~但是4KB每次http来回过程中都携带,明显让你的加载慢死,它慢就慢吧,关键是只有4KB还带不来任何别的实质性的好处。
那么在web端我们真正需要的是怎么样的存储呢? - 好大一块地盘可以用来存货,最好是一整个码头(自己YY的,不喜勿喷) - 这个地盘只要在用户代理这一端维护就可以了 - 无论页面是否重新加载,这块地盘上的数据是持久的 - 当然,可不许像cookie一样sx还要来回于server之间,那样就只是个更傻的大个而已
史前时期的web本地存储解决方案
- IE userData:允许每个站点存64KB数据,对于信任站点(intranet)这个值可以是640KB,它是借助DHTML的behaviour属性来存储本地数据的。
- Adobe Flash cookie:2002年,跟着Flash6出来的本来应该被成为本地共享对象(LSO)的一个特性,却被戏称为Flash cookie了。这个特性允许Flash对象可以存储达100KB大小的数据/domain。Brad Neuberg开发的Flash转Js的原型:AMASS(Ajax Massive Sotrage System)允许Js获取LSOs,并且集成到Dojo Toolkit中。这个后来成为dojox.storage。这个项目到2009年时可以自动检测基于Flash,Gears,Adobe AIR以及HTML5早期原型的存储。
- Google Gears:2007年,Google发布Gears,一款开源的浏览器插件,内置了一套基于SQLite的嵌入式SQL数据库系统,并且提供了统一的API对其进行访问。2010年停止更新。
综上所述,每个模式总是争对单一的浏览器提出的解决方案,或者是依赖于第三方插件,提供的API都是不相同的,存储的限制也不尽相同,更别说大为迥异的用户体验了。所以这就是HTML5闪亮登场想要拯救世界的感觉,啪,HTML5本地存储出现了--提供一套标准的API,不需要依赖第三方插件而且在各种浏览器上的本地实现保持高度的一致性。
HTML5存储简述
HTML5 Storage
起源时是另外一个名字,叫 Web Storage
,曾经一度是HTML5规范的一部分,但是由于某些原因这个被和谐出了规范之外。浏览器供应商常常将其称为 Local Storage
或是 DOM Storage
。后来由于各种别的因素,或是相似的名称/标准的出现,让HTML5的存储的名字变得更加诡异。
那么到底什么是HTML5存储呢?简而言之,就是一种让网页可以在客户端浏览器中以键值对的形式将数据存在本地。这方面,它看起来就像cookie,就算你从网站离开,把该死的tab关掉甚至杀死浏览器,等你再开的时候,咦哈,它还是在的。
和cookie不同的是,HTML5存储的东西,永远不会跟随每次http请求而来返于client与server之间。当然啦,除非你手欠,自己手动去send。
另外,和上一节中提到的其他几种“永久性”的本地存储不同,HTML5存储是在web浏览器端实现的,兼容性好呀,各种浏览器上跑都没有问题,那具体有哪些浏览器支持捏?看下列表:
IE 8.0+
FF 3.5+
Safari 4.0+
Chrome 4.0+
Opera 10.5+
iPhone 2.0+
Android 2.0+
当然啦,最终要一点,自己写脚本的时候是通过 localStorage
这个对象去操作的,这个对象是全局对象window的一个属性。在你写脚本调用它前,你应该先测下浏览器是不是支持这一新的对像。
HTML5 Storage检测:
function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
当然,你也可以在你的html代码中预先包括Modernizr,那样你就可以调用它去检测:
if (Modernizr.localStorage) {
// window.localStorage is available!
} else {
// no native support for HTML5 storage :(
// maybe try dojox.storage or a third-party solution
}
把HTML5存储耍起来
HTML5存储是基于键值对的。存:给个key往里存;取:通过上面新设置的那个key往外取。这个key是一个string,而对应于这个key的值(data)可以是任何Js支持的类型,如 String, Boolean, Number
,甚至是对象、数组等。但是无论你存入的是什么类型的,实际上,从HTML5的角度来看,它都是将它们以 string
的类型进行存储。如果你想要存取的数据是非 string
的,那你就得使用诸如 parseInt()
parseFloat()
这样的函数来强行将你的数据搞成想要的类型。HTML5对于存储的API是这么定义的:
interface Storage {
getter any getItem(in DOMString key);
setter creator void setItem(in DOMString, in any data);
};
往 setItem()
里传入已有的key进行调用的方式不会报错哦,会悄悄地重写上一个值。而通过 getItem()
想去取一个还不存在的键值对,那会返回 null
,也不报错。
你可以将 localStorage
就当作Js的其它对象一样,看作是关联数组,那样你可以用 []
操作符去替代上面说到的 getItem() | setItem()
了。看下面的例子,它们干了同样的事情。
var foo = localStorage.getItem("bar");
// ...
localStorage.setItem("bar", foo);
等价于 -->
var foo = localStorage["bar"];
// ...
localStorage["bar"] = foo;
HTML5存储还提供了另外一个API,用于将所有的存储数据杀掉(清除)。
interface Storage {
deleter void removeItem(in DOMString key);
void clear();
};
传入任何尚未存在(定义)的key来调用 removeItem()
将不会发生任何操作。
最后,第四个API,用来获得存储的数据值的总数,通过去遍历所有的key值,如下:
interface Storage {
readonly attribute unsigned long length;
getter DOMString key(in unsigned long index);
};
你可以通过给 key()
传入一个数值(落在0-(length-1)之间的值)来查看这个index对应的键名是什么。当然,如果传进来的值不在这个区间,那么函数返回 null
。
追踪HTML5存储区发生的改变
如果你想跟踪存储区域的变化,你可以通过设置 storage
这个事件来实现。在 setItem()
removeItem()
clear()
被调用且改变了存储的数据的时候,这个事件在window对象下被触发。如果以上三个方法没有实际上改变data,那就不触发事件。
在浏览器中,只要支持 localStorage
就支持 storage
事件,当然也包括IE8。当时IE8有个坑爹的就是不支持标准的事件绑定 addEventListener
(IE9支持了)。所以,要想绑上该事件,需要检测浏览器支持哪种事件绑定机制。
PS:如果之前你做过类似的了,那么你可以跳开以下部分了。当然你也可以用jQuery库来做这样的绑定。
if (window.addEventListener) {
window.addEventListener("storage", handle_storage, false);
} else {
window.attachEvent("onstorage", handle_storage);
};
function handle_storage(e) {
if (!e) {
e = window.event;
}
// At this point, e will be a StorageEvent object
// do stuff with e
}
如上所述,给进的参数 e
会是一个 StorageEvent
类型的对象,它具有如下属性。
- key --> string type
- oldValue --> any type
- newValue --> any type
- url --> string type
最后,说一下: storage
这个事件是不能由我们手动在 handle_storage
这个回调函数里去阻止它产生影响的。这个事件就好像是浏览器很直接在跟我们说:“hey,伙计,存储的数据改变了,但你Y没有任何可以做的,我只是知会你一声啊!”
现代浏览器那些得瑟的龟毛
在前面介绍关于HTML5存储出现前的那些hack的存储方法的毛病的时候,我并没有提及HTML5存储的界限在哪里。其实它也是有毛病的,可以用如下三个关键词概括:
5 megabytes
QUOTA_EXCEEDED_ERR
no
第一个,就是每个域默认情况下获得的存储空间的大小,5MB。这在所有主流浏览器上的实现都是一致的,清一色的5MB。
第二个是在超出5MB限制时抛出的异常提示。
第三个,可以通过设置这个值,来允许用户去申请更多更大的空间。
用HTML5存储真的干一场吧
这一节的实际操作的例子需要在了解前几章介绍canvas的例子,所以,to be continued。
除了键值对还能怎么搞
这一节,我们来讲讲能和HTML5抗衡的一些别的本地存储的实现。
Web SQL Database, 也叫做WebDB,是自Google发布Gears以及浏览器中内嵌SQLite以来的一种发展。它其实是在SQLite数据库上包了一层薄薄的(可能比Durex薄点),这样来允许你通过Js代码去做数据库操作。但是,你们懂的,薄,总是要出事的。
openDatabase('documents', '1.0', 'Local document storage', 5*1024*1024, function(db) {
db.changeVersion('', '1.0', function(t) {
t.executeSql('CREATE TABLE docids (id, name)');
}, error);
});
正如上面所show的,所有的操作其实是寄宿在你传进去给 executeSql()
函数的字符串参数。这个字符串可以是任何SQL语法支持的语句,比如 SELECT
, UPDATE
, INSERT
, DELETE
等。这些操作就跟后台的数据库操作没什么差别,唯一不同的就是你现在是用Js代码去调用,操作了。哈哈,爽吧。
Web SQL DB
的规范已经在如下浏览器及平台上实现了。
IE
FF
Safari 4.0+
Chrome 4.0+
Opera 10.5+
iPhone 3.0+
Android 2.0+
Indexed Database,也叫做WebSimpleDB,现在一般普遍称其为IndexedDB。IndexedDb
暴露给开发者的事一个叫做 object store
的东西。它其实和SQL数据库有很多相通的地方。也是一个基于记录与事务的数据库。
一些福利
以下是一些链接,推荐看看:
后续可能会继续更新相关推荐阅读。