├── README.md ├── store.js └── store_encode.js /README.md: -------------------------------------------------------------------------------- 1 | # store 2 | ## localstorage本地缓存方案 3 | 4 | ### 解决数据过期问题 5 | 6 | 本地实际应用建议绑定当前的用户,解决不同用户看到不同的缓存,可以做下适当的包装,比如: 7 | 8 | ```javascript 9 | setCache:function(key,value,exp){ 10 | //过期时间默认1天 11 | exp=exp||86400; 12 | store.set(key+'_'+curUserID, value,exp); 13 | 14 | }, 15 | 16 | getCache:function(key){ 17 | return store.get(key+'_'+curUserID); 18 | } 19 | ``` 20 | 21 | ### todo: 22 | 23 | 另外还又可以尝试结合一些简单的加解密技术,来保证客户端缓存数据的相对安全,但势必会造成性能上的损失。 24 | 25 | 数据加密已经完成,具体见:store_code.js,对数据的存储和获取分别做了加解密。 26 | 27 | ## 简单API 28 | 29 | ### 设置缓存 30 | ```javascript 31 | //简单的缓存 32 | store.set('a',{a:1,b:2}) 33 | 34 | //增加过期时间:单位是秒 35 | store.set('a',{a:1,b:2},3600) 36 | 37 | //清除指定缓存 38 | store.set('a',null) 39 | //或者 40 | sotre.set('a');//为了代码的可读性,不建议这样使用 41 | ``` 42 | 43 | ### 获取缓存的数据 44 | ```javascript 45 | store.get('a') 46 | //log:{a:1,b:2} 47 | ``` 48 | 49 | ### 删除指定缓存 50 | ```javascript 51 | store.del('a'); 52 | ``` 53 | 54 | ### 删除所有过期的缓存 55 | ```javascript 56 | store.clearExp(); 57 | ``` 58 | 59 | ### 清除所有缓存 60 | ```javascript 61 | store.clearAll() 62 | ``` 63 | 64 | ### 添加一个缓存 65 | ```javascript 66 | //和set的区别是,这个会检测是否已经存在同名的缓存,如果存在,就不做任何处理 67 | // 单set是会覆盖当前值的 68 | store.add('b',{c:1,d:1},3600) 69 | ``` 70 | ### 设置缓存的过期时间 71 | ```javascript 72 | //过期时间由原来的1小时,变成了2小时,并且是从创建时间计算的 73 | store.setExp('a',7200) 74 | ``` 75 | 76 | ### 读取缓存的过期时间 77 | ```javascript 78 | store.getExp('a') 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- /store.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | root.store = factory(); 8 | } 9 | }(this, function () { 10 | "use strict"; 11 | 12 | var _maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC'), 13 | _serialize=function (item) { 14 | return JSON.stringify(item); 15 | }, 16 | _deserialize=function (data) { 17 | return data && JSON.parse(data); 18 | }, 19 | _isSupported=function(storage) { 20 | var supported = false; 21 | if (storage && storage.setItem ) { 22 | supported = true; 23 | var key = '__' + Math.round(Math.random() * 1e7); 24 | try { 25 | storage.setItem(key, key); 26 | storage.removeItem(key); 27 | } catch (err) { 28 | supported = false; 29 | } 30 | } 31 | return supported; 32 | }, 33 | _getStorage=function(storage) { 34 | if (typeof(storage) === 'string') { 35 | return window[storage]; 36 | } 37 | return storage; 38 | }, 39 | _isDate=function(date) { 40 | return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime()); 41 | }, 42 | _getExpDate=function(expires, now) { 43 | now = now || new Date(); 44 | if (typeof expires === 'number') { 45 | expires = expires === Infinity ? 46 | _maxExpireDate : new Date(now.getTime() + expires * 1000); 47 | } else if (typeof expires === 'string') { 48 | expires = new Date(expires); 49 | } 50 | if (expires && !_isDate(expires)) { 51 | throw new Error('`expires` parameter cannot be converted to a valid Date instance'); 52 | } 53 | return expires; 54 | }, 55 | _cacheItem=function(value, exp) { 56 | exp = exp || _maxExpireDate; 57 | return { 58 | c : new Date().getTime(), 59 | e : _getExpDate(exp).getTime(), 60 | v: value 61 | } 62 | }; 63 | 64 | 65 | 66 | function Store() { 67 | this.storage = _getStorage('localStorage'); 68 | } 69 | 70 | //默认不支持localStorage的不报错,包含隐私模式下同样的不报错,让页面正常运行 71 | Store.prototype={ 72 | constructor:Store, 73 | set: function () {}, 74 | get: function () {}, 75 | del: function () {}, 76 | clearExp:function(){}, 77 | clearAll:function(){}, 78 | add:function(){}, 79 | setExp:function(){}, 80 | getExp:function(){} 81 | }; 82 | 83 | if (_isSupported(_getStorage('localStorage'))) { 84 | Store.prototype={ 85 | constructor:Store, 86 | /** 87 | * 设置缓存,可以设置过期时间,单位:秒 88 | * @param key 缓存名称 89 | * @param val 缓存的值,如果未定义或为null,则删除该缓存 90 | * @param exp 缓存的过期时间 91 | * @returns {*} 92 | */ 93 | set: function(key, val, exp) { 94 | if (typeof(key) !== 'string') { 95 | console.warn(key + ' used as a key, but it is not a string.'); 96 | key = ''+key; 97 | } 98 | if (val === undefined || val===null) { 99 | return this.del(key); 100 | } 101 | try { 102 | this.storage.setItem(key, _serialize(_cacheItem(_serialize(val), exp))); 103 | } catch (e) { 104 | console.error(e); 105 | } 106 | return val; 107 | }, 108 | 109 | /** 110 | * 获取缓存,如果已经过期,会主动删除缓存,并返回null 111 | * @param key 缓存名称 112 | * @returns {*} 缓存的值,默认已经做好序列化 113 | */ 114 | get: function (key) { 115 | var item = _deserialize(this.storage.getItem(key)); 116 | if(item !== null && item !== undefined) { 117 | var timeNow = new Date().getTime(); 118 | if(timeNow < item.e) { 119 | return _deserialize(item.v); 120 | } else { 121 | this.del(key); 122 | } 123 | } 124 | return null; 125 | }, 126 | 127 | /** 128 | * 删除指定的缓存 129 | * @param key 要删除缓存的主键 130 | * @returns {*} 131 | */ 132 | del: function (key) { 133 | this.storage.removeItem(key); 134 | return key; 135 | }, 136 | 137 | /** 138 | * 删除所有过期的缓存 139 | * @returns {Array} 140 | */ 141 | clearExp: function() { 142 | var length = this.storage.length, 143 | caches = [], 144 | _this = this; 145 | for (var i = 0; i < length; i++) { 146 | var key = this.storage.key(i), 147 | item = _deserialize(this.get(key)); 148 | 149 | if(item && !item.e) { 150 | if(new Date().getTime() >= item.e) { 151 | caches.push(key); 152 | } 153 | } 154 | } 155 | caches.forEach(function(key) { 156 | _this.del(key); 157 | }); 158 | return caches; 159 | }, 160 | /** 161 | * 清空缓存 162 | */ 163 | clearAll: function () { 164 | this.storage.clear(); 165 | }, 166 | /** 167 | * 添加一条缓存,如果已经存在同样名称的缓存,则不做任何处理 168 | * @param key 缓存的名称 169 | * @param value 缓存的值 170 | * @param exp 过期时间 单位:秒 171 | */ 172 | add: function (key, value, exp) { 173 | if(this.get(key) === null || this.get(key) === undefined) { 174 | this.set(key, value, exp); 175 | } 176 | }, 177 | /** 178 | * 设置修改缓存的过期时间 179 | * @param key 缓存的名称 180 | * @param exp 新的过期时间 单位:秒 181 | */ 182 | setExp: function (key, exp) { 183 | var item = this.get(key); 184 | if(item !== null && item !== undefined) { 185 | item.e = _getExpDate(exp,item.c); 186 | this.set(key,_serialize(item),exp); 187 | } 188 | }, 189 | /** 190 | * 获取缓存的过期时间 191 | * @param key 缓存的名称 192 | * @returns {*} 单位:秒 193 | */ 194 | getExp: function(key){ 195 | var item =this.get(key); 196 | return item && item.e; 197 | } 198 | } 199 | } 200 | return new Store(); 201 | })); -------------------------------------------------------------------------------- /store_encode.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | root.store = factory(); 8 | } 9 | }(this, function () { 10 | "use strict"; 11 | 12 | var _maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC'), 13 | _serialize=function (item) { 14 | return JSON.stringify(item); 15 | }, 16 | _deserialize=function (data) { 17 | return data && JSON.parse(data); 18 | }, 19 | _isSupported=function(storage) { 20 | var supported = false; 21 | if (storage && storage.setItem ) { 22 | supported = true; 23 | var key = '__' + Math.round(Math.random() * 1e7); 24 | try { 25 | storage.setItem(key, key); 26 | storage.removeItem(key); 27 | } catch (err) { 28 | supported = false; 29 | } 30 | } 31 | return supported; 32 | }, 33 | _getStorage=function(storage) { 34 | if (typeof(storage) === 'string') { 35 | return window[storage]; 36 | } 37 | return storage; 38 | }, 39 | _isDate=function(date) { 40 | return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime()); 41 | }, 42 | _getExpDate=function(expires, now) { 43 | now = now || new Date(); 44 | if (typeof expires === 'number') { 45 | expires = expires === Infinity ? 46 | _maxExpireDate : new Date(now.getTime() + expires * 1000); 47 | } else if (typeof expires === 'string') { 48 | expires = new Date(expires); 49 | } 50 | if (expires && !_isDate(expires)) { 51 | throw new Error('`expires` parameter cannot be converted to a valid Date instance'); 52 | } 53 | return expires; 54 | }, 55 | _cacheItem=function(value, exp) { 56 | exp = exp || _maxExpireDate; 57 | return { 58 | c : new Date().getTime(), 59 | e : _getExpDate(exp).getTime(), 60 | v: compress(value) 61 | } 62 | }; 63 | 64 | 65 | 66 | function Store() { 67 | this.storage = _getStorage('localStorage'); 68 | } 69 | 70 | //默认不支持localStorage的不报错,包含隐私模式下同样的不报错,让页面正常运行 71 | Store.prototype={ 72 | constructor:Store, 73 | set: function () {}, 74 | get: function () {}, 75 | del: function () {}, 76 | clearExp:function(){}, 77 | clearAll:function(){}, 78 | add:function(){}, 79 | setExp:function(){}, 80 | getExp:function(){} 81 | }; 82 | 83 | if (_isSupported(_getStorage('localStorage'))) { 84 | Store.prototype={ 85 | constructor:Store, 86 | /** 87 | * 设置缓存,可以设置过期时间,单位:秒 88 | * @param key 缓存名称 89 | * @param val 缓存的值,如果未定义或为null,则删除该缓存 90 | * @param exp 缓存的过期时间 91 | * @returns {*} 92 | */ 93 | set: function(key, val, exp) { 94 | if (typeof(key) !== 'string') { 95 | console.warn(key + ' used as a key, but it is not a string.'); 96 | key = ''+key; 97 | } 98 | if (val === undefined || val===null) { 99 | return this.del(key); 100 | } 101 | try { 102 | this.storage.setItem(key, _serialize(_cacheItem(_serialize(val), exp))); 103 | } catch (e) { 104 | console.error(e); 105 | } 106 | return val; 107 | }, 108 | 109 | /** 110 | * 获取缓存,如果已经过期,会主动删除缓存,并返回null 111 | * @param key 缓存名称 112 | * @returns {*} 缓存的值,默认已经做好序列化 113 | */ 114 | get: function (key) { 115 | var item = _deserialize(this.storage.getItem(key)); 116 | if(item !== null && item !== undefined) { 117 | var timeNow = new Date().getTime(); 118 | if(timeNow < item.e) { 119 | return _deserialize(deCompress(item.v)); 120 | } else { 121 | this.del(key); 122 | } 123 | } 124 | return null; 125 | }, 126 | 127 | /** 128 | * 删除指定的缓存 129 | * @param key 要删除缓存的主键 130 | * @returns {*} 131 | */ 132 | del: function (key) { 133 | this.storage.removeItem(key); 134 | return key; 135 | }, 136 | 137 | /** 138 | * 删除所有过期的缓存 139 | * @returns {Array} 140 | */ 141 | clearExp: function() { 142 | var length = this.storage.length, 143 | caches = [], 144 | _this = this; 145 | for (var i = 0; i < length; i++) { 146 | var key = this.storage.key(i), 147 | item = _deserialize(this.get(key)); 148 | 149 | if(item && !item.e) { 150 | if(new Date().getTime() >= item.e) { 151 | caches.push(key); 152 | } 153 | } 154 | } 155 | caches.forEach(function(key) { 156 | _this.del(key); 157 | }); 158 | return caches; 159 | }, 160 | /** 161 | * 清空缓存 162 | */ 163 | clearAll: function () { 164 | this.storage.clear(); 165 | }, 166 | /** 167 | * 添加一条缓存,如果已经存在同样名称的缓存,则不做任何处理 168 | * @param key 缓存的名称 169 | * @param value 缓存的值 170 | * @param exp 过期时间 单位:秒 171 | */ 172 | add: function (key, value, exp) { 173 | if(this.get(key) === null || this.get(key) === undefined) { 174 | this.set(key, value, exp); 175 | } 176 | }, 177 | /** 178 | * 设置修改缓存的过期时间 179 | * @param key 缓存的名称 180 | * @param exp 新的过期时间 单位:秒 181 | */ 182 | setExp: function (key, exp) { 183 | var item = this.get(key); 184 | if(item !== null && item !== undefined) { 185 | item.e = _getExpDate(exp,item.c); 186 | this.set(key,_serialize(item),exp); 187 | } 188 | }, 189 | /** 190 | * 获取缓存的过期时间 191 | * @param key 缓存的名称 192 | * @returns {*} 单位:秒 193 | */ 194 | getExp: function(key){ 195 | var item =this.get(key); 196 | return item && item.e; 197 | } 198 | } 199 | } 200 | return new Store(); 201 | })); 202 | 203 | 204 | (function(root){ 205 | 206 | function toUnicode(str){ 207 | if(!str) return ''; 208 | return str.toString().replace(/[^\x00-\xff]/g,function(s){ 209 | return '\\u'+s.charCodeAt(0).toString(16); 210 | }); 211 | } 212 | 213 | function unicode2Char(str){ 214 | if(!str) return ''; 215 | return str.replace(/\\u(\w{1,4})/g,function(a,s){ 216 | return String.fromCharCode(parseInt(s,16)); 217 | }); 218 | } 219 | 220 | root.compress=function(strNormalString) { 221 | var strCompressedString = "", 222 | intLeftOver = 0, 223 | intOutputCode = 0, 224 | pcode = 0, 225 | ccode = 0, 226 | k = 0, 227 | strNormalString=toUnicode(strNormalString), 228 | len = strNormalString.length, 229 | getCode=String.fromCharCode; 230 | 231 | for (var i = 0; i < len; i++) { 232 | ccode = strNormalString.charCodeAt(i); 233 | k = (pcode << 8) | ccode; 234 | intLeftOver += 12; 235 | intOutputCode <<= 12; 236 | intOutputCode |= pcode; 237 | pcode = ccode; 238 | if (intLeftOver >= 16) { 239 | strCompressedString += getCode(intOutputCode >> (intLeftOver - 16)); 240 | intOutputCode &= (Math.pow(2, (intLeftOver - 16)) - 1); 241 | intLeftOver -= 16; 242 | } 243 | } 244 | 245 | if (pcode != 0) { 246 | intLeftOver += 12; 247 | intOutputCode <<= 12; 248 | intOutputCode |= pcode; 249 | } 250 | 251 | if (intLeftOver >= 16) { 252 | strCompressedString += getCode(intOutputCode >> (intLeftOver - 16)); 253 | intOutputCode &= (Math.pow(2, (intLeftOver - 16)) - 1); 254 | intLeftOver -= 16; 255 | } 256 | 257 | if (intLeftOver > 0) { 258 | intOutputCode <<= (16 - intLeftOver); 259 | strCompressedString += getCode(intOutputCode); 260 | } 261 | 262 | return strCompressedString; 263 | }; 264 | root.deCompress=function(strCompressedString){ 265 | var strNormalString = "", 266 | ht = [], 267 | used = 128, 268 | intLeftOver = 0, 269 | intOutputCode = 0, 270 | ccode = 0, 271 | pcode = 0, 272 | key = 0, 273 | len = strCompressedString.length, 274 | getCode=String.fromCharCode; 275 | 276 | for (var i = 0; i < used; i++) { 277 | ht[i] = getCode(i); 278 | } 279 | 280 | 281 | for (i = 0; i < len; i++) { 282 | intLeftOver += 16; 283 | intOutputCode <<= 16; 284 | intOutputCode |= strCompressedString.charCodeAt(i); 285 | 286 | while (1) { 287 | if (intLeftOver >= 12) { 288 | ccode = intOutputCode >> (intLeftOver - 12); 289 | if (typeof (key = ht[ccode]) != "undefined") { 290 | strNormalString += key; 291 | if (used > 128) { 292 | ht[ht.length] = ht[pcode] + key.substr(0, 1); 293 | } 294 | pcode = ccode; 295 | }else { 296 | key = ht[pcode] + ht[pcode].substr(0, 1); 297 | strNormalString += key; 298 | ht[ht.length] = ht[pcode] + key.substr(0, 1); 299 | pcode = ht.length - 1; 300 | } 301 | used++; 302 | intLeftOver -= 12; 303 | intOutputCode &= (Math.pow(2, intLeftOver) - 1); 304 | }else { 305 | break; 306 | } 307 | } 308 | } 309 | return unicode2Char(strNormalString.substr(1,strNormalString.length-2)); 310 | } 311 | }(this)); 312 | --------------------------------------------------------------------------------