天天看点

JS惰性删除和定时删除可过期的localStorage缓存,或sessionStorage缓存

localStorage简介

使用localStorage可以在浏览器中存储键值对的数据。经常被和localStorage一并提及的是sessionStorage,它们都可以在当浏览器中存储键值对的数据。但是它们之间的区别是:存储在localStorage的数据可以长期保留;而当页面会话结束(也就是当页面被关闭)时,存储在sessionStorage的数据会被清除。

另外需要注意的是,localStorage中的键值对总是以字符串的形式存储,并且只能访问当前域名下的数据,不能跨域名访问。

欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。

localStorage方法

可以通过​

​setItem​

​方法增加了一个键值对数据,比如:

localStorage.setItem('name', 'OneMore');
      

如果该键已经存在,那么该键对应的值将被覆盖。还可以使用​

​getItem​

​方法读取对应键的值数据,比如:

var name = localStorage.getItem('name');
      

可以使用​

​removeItem​

​方法移除对应的键,比如:

localStorage.removeItem('name');
      

也可以使用​

​clear​

​方法移除当前域名下所有的键值对数据,比如:

localStorage.clear();
      

可过期的localStorage缓存

正如上面所提到的,localStorage只能用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。所以要实现可过期的localStorage缓存的中重点就是:如何清理过期的缓存?

惰性删除

惰性删除是指,某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。我们先来简单实现一下:

var lsc = (function (self) {
var prefix = 'one_more_lsc_'
/**
     * 增加一个键值对数据
     * @param key 键
     * @param val 值
     * @param expires 过期时间,单位为秒
     */
self.set = function (key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
    };
/**
     * 读取对应键的值数据
     * @param key 键
     * @returns {null|*} 对应键的值
     */
self.get = function (key) {
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
        }
val = JSON.parse(val);
if (val.expires < new Date().getTime()) {
localStorage.removeItem(key);
return null;
        }
return val.val;
    };
return self;
}(lsc || {}));
      

上述代码通过惰性删除已经实现了可过期的localStorage缓存,但是也有比较明显的缺点:如果一个key一直没有被用到,即使它已经过期了也永远存放在localStorage。为了弥补这样缺点,我们引入另一种清理过期缓存的策略。

定时删除

定时删除是指,每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage空间的浪费。

每隔一秒执行一次定时删除,操作如下:

  1. 随机测试20个设置了过期时间的key。
  2. 删除所有发现的已过期的key。
  3. 若删除的key超过5个则重复步骤1,直至重复500次。

具体实现如下:

var lsc = (function (self) {
var prefix = 'one_more_lsc_'
var list = [];
//初始化list
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍历所有localStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可过期缓存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
            }
        }
list = temp;
    };
self.init();
self.check = function () {
if (!list || list.length == 0) {
return;
        }
var checkCount = 0;
while (checkCount < 500) {
var expireCount = 0;
//随机测试20个设置了过期时间的key
for (var i = 0; i < 20; i++) {
if (list.length == 0) {
break;
                }
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
//从list中删除被惰性删除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
                }
val = JSON.parse(val);
//删除所有发现的已过期的key
if (val.expires < new Date().getTime()) {
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
                }
            }
//若删除的key不超过5个则跳出循环
if (expireCount <= 5 || list.length == 0) {
break;
            }
checkCount++;
        }
    }
//每隔一秒执行一次定时删除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));
      

完整源码及使用示例

完整源码及使用示例已上传到​​我的GitHub​​(https://github.com/heihaozi/LocalStorageCache)上,感谢各位小伙伴的Star和Fork。

总结

使用一种策略可能会有自己的缺点,为了规避相应的缺点,我们可以合理运用多种策略,扬长避短就得到接近完美的解决方案。

​setItem​

localStorage.setItem('name', 'OneMore');
      

​getItem​

var name = localStorage.getItem('name');
      

​removeItem​

localStorage.removeItem('name');
      

​clear​

localStorage.clear();
      

var lsc = (function (self) {
var prefix = 'one_more_lsc_'
/**
     * 增加一个键值对数据
     * @param key 键
     * @param val 值
     * @param expires 过期时间,单位为秒
     */
self.set = function (key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
    };
/**
     * 读取对应键的值数据
     * @param key 键
     * @returns {null|*} 对应键的值
     */
self.get = function (key) {
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
        }
val = JSON.parse(val);
if (val.expires < new Date().getTime()) {
localStorage.removeItem(key);
return null;
        }
return val.val;
    };
return self;
}(lsc || {}));
      

var lsc = (function (self) {
var prefix = 'one_more_lsc_'
var list = [];
//初始化list
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍历所有localStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可过期缓存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
            }
        }
list = temp;
    };
self.init();
self.check = function () {
if (!list || list.length == 0) {
return;
        }
var checkCount = 0;
while (checkCount < 500) {
var expireCount = 0;
//随机测试20个设置了过期时间的key
for (var i = 0; i < 20; i++) {
if (list.length == 0) {
break;
                }
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
//从list中删除被惰性删除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
                }
val = JSON.parse(val);
//删除所有发现的已过期的key
if (val.expires < new Date().getTime()) {
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
                }
            }
//若删除的key不超过5个则跳出循环
if (expireCount <= 5 || list.length == 0) {
break;
            }
checkCount++;
        }
    }
//每隔一秒执行一次定时删除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));