什麼是localstorage
前幾天在舊項目中發現有對cookie的操作覺得很奇怪,諮詢下來是要緩存一些信息,以避免在URL上面傳遞參數,但沒有考慮過cookie會帶來什麼問題:
① cookie大小限制在4k左右,不適合存業務資料
② cookie每次隨HTTP事務一起發送,浪費頻寬
我們是做行動專案的,所以這裡真實適合使用的技術是localstorage,localstorage可以說是對cookie的優化,使用它可以方便在客戶端儲存數據,並且不會隨著HTTP傳輸,但也不是沒有問題:
① localstorage大小限制在500萬字符左右,各個瀏覽器不一致
② localstorage在隱私模式下不可讀取
③ localstorage本質是在隱私模式下不可讀取
③ localstorage本質是在隱私模式下不可讀取
③ localstorage本質是在寫入檔案一次將資料導入內存,想想就覺得嚇人啊)
④ localstorage不能被爬蟲爬取,不要用它完全取代URL傳參
瑕不掩瑜,以上問題皆可避免,所以我們的關注點應該放在如何使用localstorage上,並且是如何正確使用。 localstorage的使用
基礎知識
localstorage儲存物件分為兩種:
① sessionStrage: session即會話的意思,這裡的session是指用戶瀏覽某個網站時,從進入網站到關閉網站這個時間段,session對象的有效期就只有這麼長。
② localStorage: 將資料保存在客戶端硬體設備上,不管它是什麼,意思就是下次開啟電腦時候資料還在。
兩者差異就是一個作為臨時保存,一個長期保存。
這裡來一段簡單的程式碼說明其基本使用:
XML/HTML Code
複製內容到剪貼簿
- div id=id=id=id
- 樣式="邊距:10px 0;邊框:1px 實心黑色;內邊距:10px;寬度:300px;
高度:100px;"-
>
div-
>
輸入 類型="文本" id="文字"
-
/> /> />/>
選擇
-
id=id=id 🎜>>
選項 值
- ="會話" >sessionStorage選項>選項>
選項
- 值=值==值=
🎜>- >本地儲存選項> > >
-
選>
-
按鈕 點擊="保存( ) ;">
- 保存資料按鈕>
-
按鈕 onclick=onclick=onclick=
;"-
>
讀取資料
按鈕-
>
腳本-
類型=類型 ">
-
var - msg
= -
文件.getElementById('msg'),
文字 = 文件
-
類型 = 文件
函數 save() {
var str = 文字.valueue;
var t = 型別.valueue;-
if (t == '會話') {
-
sessionStorage.setItem('msg', str);
-
} 其他 {
-
localStorage.setItem('msg', str);
-
}
-
}
-
-
函數 load() {
-
var t = 型別.valueue;
if (-
t == '會話') {
-
msg.innerHTML = sessionStorage
} 其他 {
- msg.innerHTML
= - localStorage
}
}
腳本- >
-
真實場景
實際工作中對本地儲存的使用一般有以下需求:
:① 儲存一般訊息,如搜尋頁的出發城市,到達城市,非即時定位資訊
【② 快取城市列表數據,這個數據往往比較大
【③每個伺服器資訊都需要可追踪,例如伺服器通知城市資料更新,這個時候在最近一次造訪的時候要自動設定過渡
四個資訊有過期日期,在過期時間需要由伺服器拉取資料
XML/HTML 程式碼
將內容複製到剪貼簿
-
define([], function () {
-
-
var Storage = _.inherit({ >
//預設屬性 -
propertys: function () { -
-
//代理對象,預設為localstorage -
-
this.sProxy = window.
- //60 * 60 * 24 * 30 * 1000
ms-
this.defaultLifeTime
= -
2592000000>🎜>
//本地快取使用以存放所有localstorage鍵值與過期日期的對應
-
this.keyCache- =
'SYSTEM”
-
//當快取容量已滿,每次刪除的快取數
this.removeNum- =
5- ;
-
},
assert: function () {
-
if (
- this.sProxy === null) {
-
throw 'not override sProxy property'; -
} -
},
-
initialize: function (opts) { -
this.propertys(); -
this.assert(); -
},
-
-
/*
-
新增localstorage
-
資料格式包含唯一鍵值,json字串,過期日期,存入日期
-
sign 為格式化後的請求參數,用於同一請求不同參數時候返回新數據,例如列表為北京的城市,後切換為上海,會判斷tag不同而更新緩存數據,tag相當於簽名
-
每一鍵值只會快取一個訊息
-
*/
-
set: function (key, value, timeout, sign) {
-
var _d = new //存入日期
-
var
- indate = _d.get>;
- //最終儲存的資料
- var entity
- = null;
if (!timeout) {
-
_d.setTime(_d.getTime() this.defaultLifeTime);
-
- timeout =
- _d.
}
- //
- this.setKeyCache(key, timeout);
- entity
- = this
-
try {
this.sProxy.setItem(key, JSON.stringify(entity));
-
return true;
-
} catch (e) {
-
//localstorage寫滿時,全清除
-
if (
- e.name == 'QuotaExceededError') {
// this.sProxy.clear(); -
//localstorage寫滿時,選擇離過期時間最近的資料刪除,因此也會有些影響,但感覺比全清除好,如果快取過多,此過程比較耗時,100ms以內
if (!this.removeLastCache()) throw '這次資料儲存量過大';
-
this.set(key, value, timeout, sign);
-
}
-
console && console.log(e);
-
}
-
return false;
-
},
-
-
//刪除過期快取
-
removeOverdueCache: function () {
-
var tmpObj = null = null
= - null
-
var now = new
- //取出鍵值對
- var cacheStr = thisache.
var
cacheMap-
= [];
var
newMap-
= [];
if (!cacheStr) {
-
return;
-
}
-
-
- cacheMap = JSON.
-
for (
- i = 0cacheMap.length; i len len
tmpObj
= -
if (tmpObj.timeout
- this.sProxy.removeItem(tmpObj.key);
} else {
newMap.push(tmpObj);
- }
- }
- this.sProxy.setItem(this.keyCache, JSON.stringify(newMap));
-
- },
-
-
removeLastCache: function () {
-
var i, len;
-
var num = this
-
//取出鍵值對
-
var
- cacheStr = thisache.
var cacheMap
= []; -
var delMap
= []; -
- //說明本次儲存過大
- if (!cacheStr) return false;
-
- cacheMap.sort(function (a, b) {
- return a.timeout - b.timeout;
- });
-
- //刪除了哪些資料
- delMap
- = cacheMap for (i =
0delMap.length; i len len;
this.sProxy.removeItem(delMap[i].key);
}
- this.sProxy.setItem(this.keyCache, JSON.stringify(cacheMap));
- return true;
- },
-
- setKeyCache: function (key, timeout) {
- if (!key || !timeout || timeout
- new
- Date().getTime() ) return;
- var i, len, tmpObj;
//取得目前已快取的鍵值字串
- var oldstr
- = this
var oldMap-
= [];
//目前key是否已存在
var
- flag = false
- var obj
- = {};
-
obj.key = 鍵;
-
obj.timeout = 逾時;
-
-
if (oldstr) {
-
oldMap = JSON.
- if (!_.isArray(oldMap)) oldMap = []; = [];
}
-
-
為(i = 0 oldMap.length; i len len;
- tmpObj = oldMap = oldMap if (
- tmpObj.key == key) { == key) { == key) {
-
oldMap[i] = obj;
-
標誌 = 休息;
-
}
-
}
-
if (!flag) oldMap.push(obj);
-
//最後將新的倉庫放到快取中
-
this.sProxy.setItem(this.keyCache, JSON.stringify(oldMap));
-
-
},
-
-
buildStorageObj:函數(數值、日期、逾時、符號){
- var
obj-
= {
值:數值,
-
逾時:逾時,
-
標誌: 標誌,
-
indate: indate
-
};
-
回 obj;
-
},
-
-
get: function (key, sign) {
-
var result, now = new
try { -
-
result = this<. class="attribute-value"> if (!result) return null;
- result
- = JSON.
//資料過期
-
if (result.timeout
-
- now) return null;
//需驗證簽章
-
if (sign) {
-
if (
- sign === result.sign)
-
return result.value;
return null;
-
} else {
-
return result.value;
-
}
-
-
} catch (e) {
-
console && console.log(e);
-
}
-
return null;
-
},
-
-
//取得簽章
-
getSign: function (key) {
-
var result,
- sign =
try {
result = this- <.> if (result) {
-
result = J.
sign- =
- }
} catch (e) {
console && console.log(e);
- }
-
返回標誌;
-
},
-
-
刪除:功能(鍵){
-
返回 this.sProxy.removeItem(key);
-
},
-
-
清除: 函數 () {
-
this.sProxy.clear();
-
}
-
});
-
-
Storage.getInstance = 函數
if (this.instance) { -
返回 this.instance; -
} 其他 { -
return -
this.instance = new 🎜>new 🎜
- }
- };
-
- 回儲存;
-
- });
潛在程式碼包含了本地儲存的基本操作,並且對以上問題進行了處理,而真實的使用還要再抽象化:
XML/HTML 程式碼
將內容複製到剪貼簿
-
define(['AbstractStorage'], function (AbstractStorage) {
-
-
var Store = _.inherit({ >
//預設屬性 -
propertys: function () { -
-
//每個物件一定要有儲存鍵,且無法重複 -
-
this.key = null;
-
//預設一條資料的生命週期,S為秒,M為分,D為天
-
this.lifeTime-
= '30M'
- //預設回傳資料
- // this.defaultData
- =
//代理對象,localstorage對象
- this.sProxy
- = new
-
},
-
setOption: function (options) {
-
_.extend(this, options);
-
},
-
-
assert: function () {
- if (
this.key-
=== null) {
-
throw 'not override key property';
-
}
if (
this.sProxy-
=== null) {
-
throw 'not override sProxy property';
-
}
},
-
-
initialize: function (opts) {
-
this.propertys();
-
this.setOption(opts);
-
this.assert();
-
},
-
-
_getLifeTime: 函數 () {
-
var 逾時 = 0;
var -
str = 這個
var 單位
- = str var num = str
-
var 地圖 = {
D: 86400,
-
小時:3600,
男:60,
-
S:1
-
};
- if (typeof
單位-
== '字串') {
-
- 單位單位 = unit.toUpperCase(); 🎜> = unit.toUpperCase(); >
}
-
逾時 = num
- num
-
如果 (單位) 逾時 = 地圖
-
//單位為毫秒
回傳 num * 逾時 * 1000 ;
}, -
-
// 快取資料 -
設定:函數(值,符號){ -
//獲得過渡時間 -
var - 逾時
= - 新
timeout.setTime(timeout.getTime() this._getLifeTime()); -
this.sProxy.set(this.key, value, timeout.getTime(), sign); -
},
//設定單一屬性 -
setAttr:函數(名稱、值、符號){ -
var key, obj; -
if (_.isObject(name)) { -
用於(鍵入姓名){ -
if (name.hasOwnProperty(key)) this.setAttr(k, name[k], value); -
- }
-
返回;
-
}
-
-
if (!sign) sign = 這個
-
//取得目前物件 -
-
obj = 這個.這個..
if (!obj) 返回; -
obj[名稱] = 值; -
this.set(obj, sign); -
-
}, -
-
getSign: 函數 () { -
返回 this.sProxy.getSign(this.key); -
}, -
-
刪除: 函數 () { -
this.sProxy.remove(this.key); -
}, -
-
removeAttr:函數(attrName){ -
var -
obj = 這 if (obj[attrName]) {
刪除 obj[attrName]; -
} -
this.set(obj); -
}, -
-
取得:函數(符號){ -
var - 結果
= [], - ,一;
var obj = this.sProxy.(m); 🎜>
var
- 類型 = typeof var o
= { 'string': true, )
-
if (o[type]) return obj;
if (_.isArray(obj)) {
-
for (var i =
0obj.length; i - len
-
len len len >
結果[i] = obj[i];
}
-
} else if (_.isObject(obj)) {
-
結果 = obj; >;
} -
-
對於(結果){ -
-
空 = 假;
休息;
-
}
-
返回! isEmpty? 結果:空;
-
},
-
-
getAttr:函數(attrName,標籤){
- var
obj-
= 這.get.
var attrVal =
null-
if (obj) {
attrVal- =
obj- = obj = obj }
-
回 attrVal;
-
}
-
-
});
-
-
Store.getInstance-
= 函數
if (this.instance) {
返回 this.instance; -
} 其他 { -
return - this.instance
= -
new 🎜>new 🎜
}
- };
-
- 回店;
- });
-
【我們真正使用的時候是使用store這個類別操作localstorage,程式碼簡單結束測試:
儲存完成,以後都不會走請求了,所以今天的基本程式碼結束了,最後在android Hybrid中有個後退按鈕,這個按鈕一旦點擊就會回到上一個頁面,這個時候裡面的localstorage就可以讀取了一個簡單的不可靠的解決方案是在webapp中加入:
XML/HTML 程式碼
將內容複製到剪貼簿
window.onunload
= -
function () { };//適合單頁應用,不要問我為什麼,我也不知道
結語
本地儲存是行動開發必要的技術點,需要深入了解,具體業務代碼後續會放到git上,有興趣的朋友可以去了解