博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HTML5本地存储Localstorage
阅读量:5281 次
发布时间:2019-06-14

本文共 10821 字,大约阅读时间需要 36 分钟。

什么是localstorage

前几天在老项目中发现有对cookie的操作觉得很奇怪,咨询下来是要缓存一些信息,以避免在URL上面传递参数,但没有考虑过cookie会带来什么问题:

① cookie大小限制在4k左右,不适合存业务数据② cookie每次随HTTP事务一起发送,浪费带宽

我们是做移动项目的,所以这里真实适合使用的技术是localstorage,localstorage可以说是对cookie的优化,使用它可以方便在客户端存储数据,并且不会随着HTTP传输,但也不是没有问题:

① localstorage大小限制在500万字符左右,各个浏览器不一致② localstorage在隐私模式下不可读取③ localstorage本质是在读写文件,数据多的话会比较卡(firefox会一次性将数据导入内存,想想就觉得吓人啊)④ localstorage不能被爬虫爬取,不要用它完全取代URL传参

瑕不掩瑜,以上问题皆可避免,所以我们的关注点应该放在如何使用localstorage上,并且是如何正确使用。

localstorage的使用

基础知识

localstorage存储对象分为两种:

① sessionStrage: session即会话的意思,在这里的session是指用户浏览某个网站时,从进入网站到关闭网站这个时间段,session对象的有效期就只有这么长。

② localStorage: 将数据保存在客户端硬件设备上,不管它是什么,意思就是下次打开计算机时候数据还在。

两者区别就是一个作为临时保存,一个长期保存。

这里来一段简单的代码说明其基本使用:

1 
3
4 5 9 11 13

真实场景

实际工作中对localstorage的使用一般有以下需求:

① 缓存一般信息,如搜索页的出发城市,达到城市,非实时定位信息

② 缓存城市列表数据,这个数据往往比较大

③ 每条缓存信息需要可追踪,比如服务器通知城市数据更新,这个时候在最近一次访问的时候要自动设置过期

④ 每条信息具有过期日期状态,在过期外时间需要由服务器拉取数据

⑤ ......

1 define([], function () {  2   3   var Storage = _.inherit({  4     //默认属性  5     propertys: function () {  6   7       //代理对象,默认为localstorage  8       this.sProxy = window.localStorage;  9  10       //60 * 60 * 24 * 30 * 1000 ms ==30天 11       this.defaultLifeTime = 2592000000; 12  13       //本地缓存用以存放所有localstorage键值与过期日期的映射 14       this.keyCache = 'SYSTEM_KEY_TIMEOUT_MAP'; 15  16       //当缓存容量已满,每次删除的缓存数 17       this.removeNum = 5; 18  19     }, 20  21     assert: function () { 22       if (this.sProxy === null) { 23         throw 'not override sProxy property'; 24       } 25     }, 26  27     initialize: function (opts) { 28       this.propertys(); 29       this.assert(); 30     }, 31  32     /* 33     新增localstorage 34     数据格式包括唯一键值,json字符串,过期日期,存入日期 35     sign 为格式化后的请求参数,用于同一请求不同参数时候返回新数据,比如列表为北京的城市,后切换为上海,会判断tag不同而更新缓存数据,tag相当于签名 36     每一键值只会缓存一条信息 37     */ 38     set: function (key, value, timeout, sign) { 39       var _d = new Date(); 40       //存入日期 41       var indate = _d.getTime(); 42  43       //最终保存的数据 44       var entity = null; 45  46       if (!timeout) { 47         _d.setTime(_d.getTime() + this.defaultLifeTime); 48         timeout = _d.getTime(); 49       } 50  51       // 52       this.setKeyCache(key, timeout); 53       entity = this.buildStorageObj(value, indate, timeout, sign); 54  55       try { 56         this.sProxy.setItem(key, JSON.stringify(entity)); 57         return true; 58       } catch (e) { 59         //localstorage写满时,全清掉 60         if (e.name == 'QuotaExceededError') { 61           //            this.sProxy.clear(); 62           //localstorage写满时,选择离过期时间最近的数据删除,这样也会有些影响,但是感觉比全清除好些,如果缓存过多,此过程比较耗时,100ms以内 63           if (!this.removeLastCache()) throw '本次数据存储量过大'; 64           this.set(key, value, timeout, sign); 65         } 66         console && console.log(e); 67       } 68       return false; 69     }, 70  71     //删除过期缓存 72     removeOverdueCache: function () { 73       var tmpObj = null, i, len; 74  75       var now = new Date().getTime(); 76       //取出键值对 77       var cacheStr = this.sProxy.getItem(this.keyCache); 78       var cacheMap = []; 79       var newMap = []; 80       if (!cacheStr) { 81         return; 82       } 83  84       cacheMap = JSON.parse(cacheStr); 85  86       for (i = 0, len = cacheMap.length; i < len; i++) { 87         tmpObj = cacheMap[i]; 88         if (tmpObj.timeout < now) { 89           this.sProxy.removeItem(tmpObj.key); 90         } else { 91           newMap.push(tmpObj); 92         } 93       } 94       this.sProxy.setItem(this.keyCache, JSON.stringify(newMap)); 95  96     }, 97  98     removeLastCache: function () { 99       var i, len;100       var num = this.removeNum || 5;101 102       //取出键值对103       var cacheStr = this.sProxy.getItem(this.keyCache);104       var cacheMap = [];105       var delMap = [];106 107       //说明本次存储过大108       if (!cacheStr) return false;109 110       cacheMap.sort(function (a, b) {111         return a.timeout - b.timeout;112       });113 114       //删除了哪些数据115       delMap = cacheMap.splice(0, num);116       for (i = 0, len = delMap.length; i < len; i++) {117         this.sProxy.removeItem(delMap[i].key);118       }119 120       this.sProxy.setItem(this.keyCache, JSON.stringify(cacheMap));121       return true;122     },123 124     setKeyCache: function (key, timeout) {125       if (!key || !timeout || timeout < new Date().getTime()) return;126       var i, len, tmpObj;127 128       //获取当前已经缓存的键值字符串129       var oldstr = this.sProxy.getItem(this.keyCache);130       var oldMap = [];131       //当前key是否已经存在132       var flag = false;133       var obj = {};134       obj.key = key;135       obj.timeout = timeout;136 137       if (oldstr) {138         oldMap = JSON.parse(oldstr);139         if (!_.isArray(oldMap)) oldMap = [];140       }141 142       for (i = 0, len = oldMap.length; i < len; i++) {143         tmpObj = oldMap[i];144         if (tmpObj.key == key) {145           oldMap[i] = obj;146           flag = true;147           break;148         }149       }150       if (!flag) oldMap.push(obj);151       //最后将新数组放到缓存中152       this.sProxy.setItem(this.keyCache, JSON.stringify(oldMap));153 154     },155 156     buildStorageObj: function (value, indate, timeout, sign) {157       var obj = {158         value: value,159         timeout: timeout,160         sign: sign,161         indate: indate162       };163       return obj;164     },165 166     get: function (key, sign) {167       var result, now = new Date().getTime();168       try {169         result = this.sProxy.getItem(key);170         if (!result) return null;171         result = JSON.parse(result);172 173         //数据过期174         if (result.timeout < now) return null;175 176         //需要验证签名177         if (sign) {178           if (sign === result.sign)179             return result.value;180           return null;181         } else {182           return result.value;183         }184 185       } catch (e) {186         console && console.log(e);187       }188       return null;189     },190 191     //获取签名192     getSign: function (key) {193       var result, sign = null;194       try {195         result = this.sProxy.getItem(key);196         if (result) {197           result = JSON.parse(result);198           sign = result && result.sign199         }200       } catch (e) {201         console && console.log(e);202       }203       return sign;204     },205 206     remove: function (key) {207       return this.sProxy.removeItem(key);208     },209 210     clear: function () {211       this.sProxy.clear();212     }213   });214 215   Storage.getInstance = function () {216     if (this.instance) {217       return this.instance;218     } else {219       return this.instance = new this();220     }221   };222 223   return Storage;224 225 });
View Code

这段代码包含了localstorage的基本操作,并且对以上问题做了处理,而真实的使用还要再抽象:

1 define(['AbstractStorage'], function (AbstractStorage) {  2   3   var Store = _.inherit({  4     //默认属性  5     propertys: function () {  6   7       //每个对象一定要具有存储键,并且不能重复  8       this.key = null;  9  10       //默认一条数据的生命周期,S为秒,M为分,D为天 11       this.lifeTime = '30M'; 12  13       //默认返回数据 14       //      this.defaultData = null; 15  16       //代理对象,localstorage对象 17       this.sProxy = new AbstractStorage(); 18  19     }, 20  21     setOption: function (options) { 22       _.extend(this, options); 23     }, 24  25     assert: function () { 26       if (this.key === null) { 27         throw 'not override key property'; 28       } 29       if (this.sProxy === null) { 30         throw 'not override sProxy property'; 31       } 32     }, 33  34     initialize: function (opts) { 35       this.propertys(); 36       this.setOption(opts); 37       this.assert(); 38     }, 39  40     _getLifeTime: function () { 41       var timeout = 0; 42       var str = this.lifeTime; 43       var unit = str.charAt(str.length - 1); 44       var num = str.substring(0, str.length - 1); 45       var Map = { 46         D: 86400, 47         H: 3600, 48         M: 60, 49         S: 1 50       }; 51       if (typeof unit == 'string') { 52         unit = unit.toUpperCase(); 53       } 54       timeout = num; 55       if (unit) timeout = Map[unit]; 56  57       //单位为毫秒 58       return num * timeout * 1000 ; 59     }, 60  61     //缓存数据 62     set: function (value, sign) { 63       //获取过期时间 64       var timeout = new Date(); 65       timeout.setTime(timeout.getTime() + this._getLifeTime()); 66       this.sProxy.set(this.key, value, timeout.getTime(), sign); 67     }, 68  69     //设置单个属性 70     setAttr: function (name, value, sign) { 71       var key, obj; 72       if (_.isObject(name)) { 73         for (key in name) { 74           if (name.hasOwnProperty(key)) this.setAttr(k, name[k], value); 75         } 76         return; 77       } 78  79       if (!sign) sign = this.getSign(); 80  81       //获取当前对象 82       obj = this.get(sign) || {}; 83       if (!obj) return; 84       obj[name] = value; 85       this.set(obj, sign); 86  87     }, 88  89     getSign: function () { 90       return this.sProxy.getSign(this.key); 91     }, 92  93     remove: function () { 94       this.sProxy.remove(this.key); 95     }, 96  97     removeAttr: function (attrName) { 98       var obj = this.get() || {}; 99       if (obj[attrName]) {100         delete obj[attrName];101       }102       this.set(obj);103     },104 105     get: function (sign) {106       var result = [], isEmpty = true, a;107       var obj = this.sProxy.get(this.key, sign);108       var type = typeof obj;109       var o = { 'string': true, 'number': true, 'boolean': true };110       if (o[type]) return obj;111 112       if (_.isArray(obj)) {113         for (var i = 0, len = obj.length; i < len; i++) {114           result[i] = obj[i];115         }116       } else if (_.isObject(obj)) {117         result = obj;118       }119 120       for (a in result) {121         isEmpty = false;122         break;123       }124       return !isEmpty ? result : null;125     },126 127     getAttr: function (attrName, tag) {128       var obj = this.get(tag);129       var attrVal = null;130       if (obj) {131         attrVal = obj[attrName];132       }133       return attrVal;134     }135 136   });137 138   Store.getInstance = function () {139     if (this.instance) {140       return this.instance;141     } else {142       return this.instance = new this();143     }144   };145 146   return Store;147 });
View Code

我们真实使用的时候是使用store这个类操作localstorage,代码结束简单测试:

存储完成,以后都不会走请求,于是今天的代码基本结束。

其它

隐私模式下可以采用window.name模拟sessionStorage的方式处理,因为window.name是可做保存的,这个也是其解决跨域方案的原因。

在android Hybrid中有一后退按钮,此按钮一旦按下会回到上一个页面,这个时候里面的localstorage可能会读取失效!一个简单不靠谱的解决方案是在webapp中加入:

window.onunload = function () { };//适合单页应用,不要问我为什么,我也不知道

结语

localstorage是移动开发必不可少的技术点,需要深入了解,具体业务代码后续会放到git上,有兴趣的朋友可以去了解

转载于:https://www.cnblogs.com/yexiaochai/p/4509472.html

你可能感兴趣的文章
Oracle数据导入Mysql中
查看>>
BZOJ-4424 &&CodeForces-19E Fairy DP+dfs (Link-Cut-Tree可A)
查看>>
MongoDB学习笔记——聚合操作之group,distinct,count
查看>>
大道至简读后感(第四章)
查看>>
IDA IDC Tutorials: Additional Auto-Commenting
查看>>
k8s-存储卷1-十二
查看>>
在Android中Intent的概念及应用(二)——Intent过滤器相关选项
查看>>
前端面试题(4)iframe有哪些优点?iframe缺点是什么?
查看>>
第十六章 多态性(一)
查看>>
INSERT IGNORE INTO / REPLACE INTO
查看>>
Python数据类型-布尔/数字/字符串/列表/元组/字典/集合
查看>>
MFC中theApp
查看>>
类的无参方法
查看>>
【刷题】SPOJ 705 SUBST1 - New Distinct Substrings
查看>>
IEEE 754浮点数表示标准
查看>>
linux下mysql的root密码忘记解决方
查看>>
提出的问题
查看>>
解决Jenkins无法编译Egret5.0项目的问题
查看>>
declare 结构用来设定一段代码的执行指令
查看>>
图解算法读书笔记
查看>>