一、Web 存储对象localStorage和sessionStorage允许我们在浏览器上保存键/值对。
- key和value都必须为字符串。
- 存储大小限制为 5MB+,具体取决于浏览器。
- 它们不会过期。
- 数据绑定到源(域/端口/协议)。
二、在页面刷新后(对于sessionStorage)甚至浏览器完全重启(对于localStorage)后,数据仍然保留在浏览器中。我们很快就会看到。
三、API:两个存储对象都提供相同的方法和属性:
- setItem(key, value)—— 存储键/值对。
- getItem(key)—— 按照键获取值。
- removeItem(key)—— 删除键及其对应的值。
- clear()—— 删除所有数据。
- key(index)—— 获取该索引下的键名。
- length—— 存储的内容的长度。
正如你所看到的,它就像一个Map集合(setItem/getItem/removeItem),但也允许通过key(index)来按索引访问。
访问方式
setItem/getItem
类对象形式访问
一、可以像使用一个普通对象那样,读取/设置键,像这样:
// 设置 key
localStorage.test = 2;
// 获取 key
alert( localStorage.test ); // 2
// 删除 key
delete localStorage.test;
1、这是历史原因造成的,并且大多数情况下都可行,但通常不建议这样做,因为:
(1)如果键是由用户生成的,那么它可以是任何内容,例如 length 或 toString ,也可以是localStorage的另一种内建方法。在这种情况下,getItem/setItem可以正常工作,而类对象访问的方式则会失败:
let key = 'length';
localStorage[key] = 5; // Error,无法对 length 进行赋值
遍历键
一、如何获取所有保存的值或键呢?因为存储对象是不可迭代的,所以有以下方法。
- for循环
- for…in + hasOwnProperty()
- for…in + Object.keys()
for循环
1、方法1:像遍历数组那样遍历它们:for(let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
for…in + hasOwnproperty
1、方法2:使用for key in localStorage循环,就像处理常规对象一样。(不可行)
(1)缺点:它会遍历所有的键,但也会输出一些我们不需要的内建字段。
2、方法3:我们需要使用hasOwnProperty检查来过滤掉原型中的字段:// 不好的尝试
for(let key in localStorage) {
alert(key); // 显示 getItem,setItem 和其他内建的东西
}
for(let key in localStorage) { if (!localStorage.hasOwnProperty(key)) { continue; // 跳过像 "setItem","getItem" 等这样的键 } alert(`${key}: ${localStorage.getItem(key)}`); }
for…in + Object.keys
1、方法4:使用Object.keys获取只属于“自己”的键,然后如果需要,可以遍历它们:let keys = Object.keys(localStorage); // Object.keys只返回属于对象的键,会忽略原型上的 for(let key of keys) { alert(`${key}: ${localStorage.getItem(key)}`); }
仅字符串
一、键和值都必须是字符串。
1、如果是任何其他类型,如数字或对象,它会被自动转换为字符串。
| 【示例】```javascript // 类对象形式 sessionStorage.user = {name: “John”}; alert(sessionStorage.user); // [object Object]
// setItem方法 sessionStorage.setItem(‘user1’, {name: ‘test’ }; alert(sessionStorage.user1); // [object Object]
 |
| --- |
<a name="sYyyz"></a>
## 对象处理
一、我们可以使用JSON来存储对象:
| 【示例】```javascript
sessionStorage.user = JSON.stringify({name: "John"});
// sometime later
let user = JSON.parse( sessionStorage.user );
alert( user.name ); // John
| | —- |
二、也可以对整个存储对象进行字符串化处理
| 【示例】出于调试目的:```javascript // 为 JSON.stringify 增加了格式设置选项,以使对象看起来更美观 alert( JSON.stringify(localStorage, null, 2) );
|
| --- |
<a name="TDiYZ"></a>
# 本地存储的空间(size)
一、浏览器存储量测试:[http://dev-test.nemikor.com/web-storage/support-test/](http://dev-test.nemikor.com/web-storage/support-test/)
| 【示例】localStorage不同浏览器支持大小的统计方法```javascript
(function() {
if(!window.localStorage) {
console.log('当前浏览器不支持localStorage!')
}
var test = '0123456789';
var add = function(num) {
num += num;
if(num.length == 10240) {
test = num;
return;
}
add(num);
}
add(test);
var sum = test;
var show = setInterval(function(){
sum += test;
try {
window.localStorage.removeItem('test');
window.localStorage.setItem('test', sum);
console.log(sum.length / 1024 + 'KB');
} catch(e) {
alert(sum.length / 1024 + 'KB超出最大限制');
clearInterval(show);
}
}, 0.1)
| | —- |
二、存储空间
- 主流浏览器(包含PC、移动端)都是5M的数量级。
- 手机QQ、手机QQ浏览器、微信中是2.5M
超过最大值的行为
一、各浏览器都抛出一个错误 QUOTA_EXCEEDED_ERR
二、firefox、opera中,用户可以自己设置本地存储的大小
localStorage
一、localStorage最主要的特点是:
1、在同源的所有标签页和窗口之间共享数据。
(1)我们只需要在同一个源(域/端口/协议),URL 路径可以不同。
(2)在所有同源的窗口之间,localStorage数据可以共享。因此,如果我们在一个窗口中设置了数据,则在另一个窗口中也可以看到数据变化。
2、数据不会过期。它在浏览器重启甚至系统重启后仍然存在。
| 【示例】```javascript // 运行此代码 localStorage.setItem(‘test’, 1);
// 然后关闭/重新打开浏览器,或者只是在不同的窗口打开同一页面,然后你可以这样获取它: alert( localStorage.getItem(‘test’) ); // 1
|
| --- |
<a name="lCtqZ"></a>
## 整站本地存储的规划
一、客户端的存储空间宝贵,然而站点也因为业务的不同,很难有一个统一的实施细则,但是有几个大原则不会变。
- 只保存重要页面的重要数据
- 典型的,首页首屏
- 对业务庞大的站点,这点尤其重要
- 极大提高用户体验的数据
- 比如表单的状态,可以提交之前保存,当用户刷新页面时可以还原
- 静态资源,比如 js 和 css
- 一个请求一个 key 值(一个 cgi 一个 key 值)
- 避免请求链接加参数的 key (http://request-ajax.cgi[params]),这样必然让 key 值趋于冗余从而撑爆空间
以上几大原则仅作参考,一切从实际业务出发。
<a name="Lat4H"></a>
## 本地存储注意事项
<a name="h8Fwg"></a>
### 不总是有用
一、目前来说,localStorage 只能做为提升用户体验的手段,而不能成为客户端逻辑的可靠的、唯一的依赖,毕竟用户环境千差万别。 当不使用通用 local 库的情况下,务必作如下检查:
```javascript
if (window.localStorage) {
try {
localStorage.setItem('bla', 'bla');
} catch (e) {
if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
// todo
} else {
// todo
}
}
}
key 过多
一、这个在上面的“原则”里面也有说,key 值应该整站统一约束。先整理如下规则,待讨论。
- 页面 path 做为 key 值,该页面上的数据统一在 value 处理
一条 cgi 一个 key 值,不同参数在 value 中处理(性能问题)
序列化
过于依赖 JSON.stringify ,性能问题,移动端尤其明显
- value 尽量使用 string ```javascript // before function store(key, val) { localStorage.setItem(key, JSON.stringify(val)); } store(‘num’, 1); store(‘on’, true); store(‘name’, ‘pamela’);
// after function store(key, val) {
localStorage.setItem(key, val);
} store(‘num’, ‘1’); store(‘on’, ‘true’); store(‘name’, ‘pamela’); ```
频繁 set/get
尽量将数据缓存进内存,然后页面卸载的时候一次写入。 ```javascript // before $(‘input[type=”checkbox”]’).click(function () { localStorage.setItem($(this).attr(‘name’), $(this).is(‘:checked’)); });
// after window.onunload = function () { $(‘input[type=”checkbox”]’).each(function () { localStorage.setItem($(this).attr(‘name’), $(this).is(‘:checked’)); }); };
<a name="MD2J8"></a>
### 阻塞UI
按钮在点击过程中会有 UI 变化,这个时候同步操作 local 就会阻塞 UI 。
```javascript
// before
$('button').click(function () {
var name = localStorage.getItem('name');
$('#name').html(name);
});
// after
$('button').click(function () {
window.setTimeout(function () {
var name = localStorage.getItem('name');
$('#name').html(name);
}, 10);
});
Storage 事件:同源的不同窗口通信
一、当localStorage或sessionStorage中的数据更新(调用setItem, removeItem, clear方法)后,storage事件就会触发,它具有以下属性:
- key—— 发生更改的数据的key(如果调用的是.clear()方法,则为null)。
- oldValue—— 旧值(如果是新增数据,则为null)。
- newValue—— 新值(如果是删除数据,则为null)。
- url—— 发生数据更新的文档的 url。
- storageArea—— 发生数据更新的localStorage或sessionStorage对象。
二、storage事件会在所有可访问到存储对象的window对象上触发,导致当前数据改变的window对象除外。
| 【示例】详细解释。
1、想象一下,你有两个窗口,它们具有相同的页面。所以localStorage在它们之间是共享的。
2、在浏览器的两个窗口中打开此页面来测试下面的代码。
3、如果两个窗口都在监听window.onstorage事件,那么每个窗口都会对另一个窗口中发生的更新作出反应。```javascript
// 在其他文档对同一存储进行更新时触发
window.onstorage = event => { // 等同于 window.addEventListener(‘storage’, () => {
if (event.key != ‘now’) return;
alert(event.key + ‘:’ + event.newValue + “ at “ + event.url); // event.url:发生数据更新的文档的 url
};
localStorage.setItem(‘now’, Date.now());
4、event.storageArea包含存储对象 —— sessionStorage 和 localStorage 具有相同的事件,所以event.storageArea引用了被修改的对象。我们可能会想设置一些东西,以“响应”更改。<br />(1)这允许同源的不同窗口交换消息。 |
| --- |
三、如果以类对象方式访问时,不会触发该事件。<br />四、现代浏览器还支持[Broadcast channel API](https://developer.mozilla.org/zh/docs/Web/api/Broadcast_Channel_API),这是用于同源窗口之间通信的特殊 API,它的功能更全,但被支持的情况不好。有一些库基于localStorage来 polyfill 该 API,使其可以用在任何地方。
<a name="dbJ45"></a>
# sessionStorage
一、sessionStorage对象的使用频率比localStorage对象低得多。<br />二、属性和方法是相同的,但是它有更多的限制:<br />1、sessionStorage的数据只存在于当前浏览器标签页。<br />(1)具有相同页面的另一个标签页中将会有不同的存储。<br />(2)但是,它在同一标签页下的 iframe 之间是共享的(假如它们来自相同的源)。<br />2、数据在页面刷新后仍然保留,但在关闭/重新打开浏览器标签页后不会被保留。
| 【示例】运行效果。```javascript
// 运行此代码
sessionStorage.setItem('test', 1);
// 然后刷新页面。这时你仍然可以获取到数据:
alert( sessionStorage.getItem('test') ); // after refresh: 1
// 如果你在另一个新的标签页中打开此页面,然后在新页面中再次运行上面这行代码,则会得到null,表示“未找到数据”
| | —- |
三、sessionStorage不仅绑定到源,还绑定在同一浏览器标签页。因此,sessionStorage很少被使用。
第三方库
- store.js :跨浏览器,ie6+ 。
- lawnchair :轻量,为移动而生。
- locache :又一通用库,优雅降级。提供超时 api 。
- lscache:A localStorage-based memcache-inspired client-side caching library.
- 自动保存表单
- Inject:CommonJS and AMD module loading, cross-origin fetching, localStorage caching, and more.
- basket.js:A script and resource loader for caching & loading files with localStorage