localStorage需要注意的地方

localStorage

作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。localStorage的存储结构实质是哈希表。

优势

  1. localStorage拓展了cookie的4K限制
  2. localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的

    局限

  3. 浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
  4. 目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
  5. localStorage在浏览器的隐私模式下面是不可读取的
  6. localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
  7. localStorage不能被爬虫抓取到

使用

localStorage(对象)只支持string类型的存储

  • JSON.stringify():将JSON转换成为JSON字符串
  • JSON.parse():将JSON字符串转换成为JSON对象
  1. 是否兼容
1
if(!window.localStorage)
  1. 写入/写入
    1
    2
    3
    localStorage["a"]=1;
    localStorage.a=1;
    localStorage.setItem("c",3);

localStorage的使用也是遵循同源策略的,所以不同的网站直接是不能共用相同的localStorage

  1. 读取
1
2
3
4
5
6
7
var a=localStorage.a;
var b=localStorage["b"];
// 3. 如果key的value不存在时,返回null
var c=localStorage.getItem("c");
// 4. 使用key()方法
// 获取第0个数据项的键名
localStorage.key(0)
  1. 删除
  • 清除当前域名下的所有localstorage数据

    1
    localStorage.clear()
  • 删除某个键值对:

    1
    localStorage.removeItem("a")

数据共享

  1. localStorage只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据
  2. sessionStorage比localStorage更严苛一点,除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下

不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。

生存期

在数据存储的时效性上,localStorage并不会像cookie那样可以设置数据存活的时限。也就是说,只要用户不主动删除,localStorage存储的数据将会永久存在。

存储位置

对于localStorage数据的存储,是存在于本地的文件系统(客户端硬件设备)中的

例如,对于chrome来说,localStorage数据的存储位置是在:C:\Users{userName}\AppData\Local\Google\Chrome\User Data\Default\Local Storage中

在 setItem 时,可能会达到大小限制,最好加上错误捕捉try/catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
try {  
localStorage.setItem(key, value);
} catch(e) {
if (isQuotaExceeded(e)) {
// Storage full, maybe notify user or do some clean-up
}
}

function isQuotaExceeded(e) {
var quotaExceeded = false;
if (e) {
if (e.code) {
switch (e.code) {
case 22:
quotaExceeded = true;
break;
case 1014:
// Firefox
if (e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
quotaExceeded = true;
}
break;
}
} else if (e.number === -2147024882) {
// Internet Explorer 8
quotaExceeded = true;
}
}
return quotaExceeded;
}

在ios设备上无法重复setItem()

另外,在iPhone/iPad上有时设置setItem()时会出现诡异的QUOTA_EXCEEDED_ERR错误,这时一般在setItem之前,先removeItem()就ok了

给localStorage设置一个过期时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function set(key,value){
var curtime = new Date().getTime();//获取当前时间
localStorage.setItem(key,JSON.stringify({val:value,time:curtime}));//转换成json字符串序列
}
function get(key,exp)//exp是设置的过期时间
{
var val = localStorage.getItem(key);//获取存储的元素
var dataobj = JSON.parse(val);//解析出json对象
if(new Date().getTime() - dataobj.time > exp)//如果当前时间-减去存储的元素在创建时候设置的时间 > 过期时间
{
console.log("expires");//提示过期
}
else{
console.log("val="+dataobj.val);
}
}

具体方法

  1. 重写set
  • 首先有三个参数 key、value、expired ,分别对应 键、值、过期时间,
  • 这个时间如何设置呢?在这个值存入的时候在键(key)的基础上扩展一个字段,如:key+’expires’,而它的值为当前 时间戳 + expired过期时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
set(key, value, expired) {
/*
* set 存储方法
* @ param {String} key 键
* @ param {String} value 值,
* @ param {String} expired 过期时间,以分钟为单位,非必须
*/
let source = this.source;
source[key] = JSON.stringify(value);
if (expired){
source[`${key}__expires__`] = Date.now() + 1000*60*expired
};
return value;
}
  1. 重写get
  • 获取数据时,先判断之前存储的时间有效期,与当前的时间进行对比;
  • 但存储时expired为非必须参数,所以默认为当前时间+1,即长期有效;
  • 如果存储时有设置过期时间,且在获取的时候发现已经小于当前时间戳,则执行删除操作,并返回空值;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
get(key) {
/*
* get 获取方法
* @ param {String} key 键
* @ param {String} expired 存储时为非必须字段,所以有可能取不到,默认为 Date.now+1
*/
const source = this.source,
expired = source[`${key}__expires__`]||Date.now()+1;
const now = Date.now();

if ( now >= expired ) {
this.remove(key);
return;
}
const value = source[key] ? JSON.parse(source[key]) : source[key];
return value;
}
  1. 重写remove
1
2
3
4
5
6
7
remove(key) {
const data = this.source
const value = data[key]
delete data[key]
delete data[`${key}__expires__`]
return value
}
  1. 将删除操作放到constructor里面(不取get的时候也可以清除)

localStorage如果不主动清除是一直存在的,也就是会占用存储空间,所以默认会在constructor里清除一次,已经过期的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

class storage {
constructor(props) {
this.props = props || {}
this.source = this.props.source || window.localStorage
this.initRun();
}
initRun(){
/*
* set 存储方法
* @ param {String} key 键
* @ param {String} value 值,存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify
* @ param {String} expired 过期时间,以分钟为单位
*/
const reg = new RegExp("__expires__");
let data = this.source;
let list = Object.keys(data);
if(list.length > 0){
list.map((key,v)=>{
if( !reg.test(key )){
let now = Date.now();
let expires = data[`${key}__expires__`]||Date.now+1;
if (now >= expires ) {
this.remove(key);
};
};
return key;
});
};
}
}

localStorage字符串最大容量是5M,那么我如果存储容量溢出了怎么办?

其实这个5M对于不同浏览器来说也是不确定的,不过大体上是一个5M的范围,溢出了怎么办,肯定会发生错误啊。浏览器会报一个名为“QuotaExceededError”的错误

最后一次溢出的字符串是会存储到最大容量停止还是不会存储?

正常情况下,可能不会存储5M的字符串,但是也不能保证浏览器日积月累的情况下,恰巧用户也没清理过缓存,那么当最后容量接近5M的时候,我们再存储一个字符串进去的时候会发生错误,发生错误的字符串是存了一半?还是压根就没存呢?答案是—没存。下面是我写的一个demo,最后发现报错的时候刷新浏览器,localStorage的当前容量为发生变化。

所以超过限制大小的时候,浏览器会:

  1. 不存储数据,也不会覆盖现有数据
  2. 引发 QUOTA_EXCEEDED_ERR 异常
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    (function(){
    var safeLocalStorage = function(key, value) {
    try{
    localStorage.setItem(key,value);
    }catch(oException){
    if(oException.name == 'QuotaExceededError'){
    console.log('已经超出本地存储限定大小!');
    // 可进行超出限定大小之后的操作,如下面可以先清除记录,再次保存
    localStorage.clear();
    localStorage.setItem(key,value);
    }
    }
    }
    this.safeLocalStorage = safeLocalStorage;
    })();
localStorage一般小于5M,是每个域5M,如果超出限制,可以:(借助postMessage和iframe来实现跨域的数据读取)
  • 申请其他域
  • 修改ng配置
  • 配置postmessage通信往其他域上存取

一般可以利用 indexedDB 存文件类型的数据,localStorage 存业务数据

获得localStorage已使用容量

1
2
3
4
5
6
7
8
9
10
11
12
(function(){
if(!window.localStorage) {
console.log('浏览器不支持localStorage');
}
var size = 0;
for(item in window.localStorage) {
if(window.localStorage.hasOwnProperty(item)) {
size += window.localStorage.getItem(item).length;
}
}
console.log('当前localStorage已使用容量为' + (size / 1024).toFixed(2) + 'KB');
})()