1. 前言
Web 应用中的数据可以保存到两个地方,一个是 Web 服务器,一个是 Web 客户端。
Web 服务器适合保存敏感信息,以及那些不希望被人篡改的数据,服务器安全可靠;然而,服务器端存储并非适合所有的网站,例如一些不太重要的信息放在用户的计算机上比较方便。比如应用状态,保存后可以方便用户在将来返回该状态。如果从服务端获取比较耗时,可以通过将其保存在用户本地来提供性能。
在 HTML5 之前,本地存储的方法是使用 cookie ,在浏览器和服务器之间传送信息,利用 cookie 保存少量数据确实方便,但是 cookie 大小受到限制,大多数浏览器对 cookie 的大小有 4096 字节的限制。另外有些用户禁用了浏览器或客户端设备接收 cookie 的能力,因此限制了这一功能。
HTML5 新增了更好的本地存储的功能,让我们在用户的计算机上保存信息更加方便,而且保存的数据可以无限期的保存在用户的计算机上,不会发送到服务器,并且能够通过简单的 js 对象操作它们,这个 Web 存储的新功能很适合开发离线应用和在本地保存用户信息。
2. Web 存储简介
Web 存储分为两种,分别对应两个 JavaScript 对象:
- 本地存储,对应 localStorage 对象,用于长期保存网站的数据,并且任何的页面都可以访问该数据。也就是说,如果有一个页面利用本地存储保存了数据,那么用户在一天后,一星期,甚至一年后该数据还是在那里,除非用户利用了浏览器的清除功能清除了数据。
- 会话存储,对应 sessionStorage 对象,用于临时保存针对一个标签页的数据,在访客关闭标签页之前,这些数据是存在的,而关闭之后就会被浏览器删除。
无论是本地存储还是会话存储,都是与网站的域联系在一起。如果是在不同域名下,就不能访问数据了。另外使用不同的浏览器,那么存储的数据也将是不同的。
需要注意的是,尽管 HTML5 没有硬性规定存储空间的上限,但是大多数浏览器都把本地存储的限制定位5MB。
2.1 存储数据
要保存数据到本地存储或会话存储中,需要为该数据规定一个名字,这个名字叫做键,可以通过键来取回数据。要保存数据,需使用 localStorage.setItem() 方法:
|
|
例如,假设你想保护用户名,那么可以使用叫做 username 的键:
|
|
读取本地存储中的数据跟保存数据一样简单,只需使用 localStorage.getItem() 方法。例如,下面这行代码可以读出前面保存的用户名:
|
|
会话存储也一样简单,唯一的区别是要使用 sessionStorage 对象,而不是 localStorage 对象。
另外 Web 存储还支持两种读写数据的语法。除了前面的方法,还可以使用属性和索引方式。用属性名时,可以通过 localStorage.username 读取,用索引方式,可以通过 localStorage[“username”] ,但是大多数情况下还是使用 getItem() 和 setItem() 最好,因为歧义最少。
需要注意的是,可能在测试的时候需要从 Web 服务器上打开的页面才能使用 Web 存储,因为浏览器要限制 Web 空间的大小。
3.深入 Web 存储
3.1 删除数据项
调用 removeItem() 方法,传入键名,就可以删除不要的数据项:
|
|
另外可以调用 clear() 方法清空本地的会话数据:
|
|
3.2 查找数据项
要搜索某一个数据项,只要知道键名即可,另外也可以使用 key 方法从本地或会话存储中取得所有的数据项。
|
|
3.3 保存数值、日期、对象
通过 localStorage 和 sessionStorage 保存数据时,该数据将自动被转换成文本字符串。
- 对于数值,可以通过 Number() 函数解决。
- 对于日期,可以先按照规定按照既定的格式日期格式转换成相应的文本,然后在根据取得的文本创建日期对象。
- 对于对象,可以利用 json 编码,在存储数据前,利用 JSON.stringigy() 方法将对象转换成文本形式,然后再传给 setItem() 方法;在取得数据后,利用 JSON.parse() 方法,将文本转换回对象。
3.4 响应 Web 存储变化
Web 存储为我们提供了在不同浏览器窗口间通信的机制,具体来说就是本地存储或会话存储发生变化时,其他查看同一页面或者同一站点的其他页面的窗口就会触发 window.onStorage 事件。
所谓存储变化,是指向存储中添加新的数据项,修改既有数据项,删除数据项或清除所有数据。但是,那些对存储不产生影响的操作(比如用既有的键名保存同样的值,或者清除原本就是空的存储空间),不会引发 onStorage 事件。
onStorage 事件可以提供发生变化的键(key)和值、原来的值(oldValue)、新的值(newValue)和导致此次变化的页面的URL(url),如果 onStorage 事件反映的是插入新数据项,那么 oldValue 属性是 null 或者空字符串。
4. 注意事项
- 在 setItem 时,可能会达到的5MB大小限制,在存储容量快满时,会造成 getItem 性能急剧下降,最好加上错误捕捉:
|
|
5. 其他用法
5.1 同源窗口通信
应用场景: 多窗口共用的一些组件,对数据实时同步有较高要求的应用场景。比如通知中心上面的未读数量,两个窗口,A 窗口更新为 8,切到 B 窗口还是 9,这就造成了体验不一致,这个例子可能还觉得无关痛痒;再比如购物车,两个产品窗口,A 窗口添加到购物车,切到 B 窗口添加到购物车,发现没有 A 添加的产品,这样就比较严重了。这当然也可以通过每个窗口都与后台建立连接来更新,但用户如果开十几个窗口就开销大了。
有了同源窗口通信,我们就可以只有一个窗口与后台建立连接,收到更新后,广播给其他窗口就可以。原理如下:
其实原理简单,每次 localStorage 中有任何变动都会触发一个 storage 事件,所有窗口都监听这个事件,一旦有窗口更新 localStorage,其他窗口都会收到通知,根据事件中的 key 把不关心的变动过滤掉,但是要实现一套完整的广播机制还是有些复杂,你需要:
管理好每个窗口的唯一 ID
防止消息重复
防止消息发给不关心的窗口
窗口keep alive
主窗口选举
…
开源实现:diy/intercom.js、tejacques/crosstab
6. 浏览器兼容性