一文梳理Web儲存,從cookie,WebStorage到IndexedDB
前言
HTTP是無狀態的協議,網路早期最大的問題之一是如何管理狀態。伺服器無法知道兩個請求是否來自同一個瀏覽器。cookie應運而生,開始出現在各大網站,然而隨著前端應用複雜度的提高,Cookie 也漸漸演化為了一個“儲存多面手”,承載了 自身僅有的4KB 記憶體所不能承受的壓力。在這樣的背景下,web Storage應運而生,專門用於瀏覽器儲存。但web Storage也僅僅是cookie的擴充套件,只能用於儲存少量的簡單資料,當遇到大規模的、結構複雜的資料時,Web Storage 也愛莫能助。這時候,就不得不其強大的IndexedDB了,一個執行在瀏覽器上的非關係型資料庫。本文從cookie講起,Web Storage過渡,IndexedDB結尾,梳理一下web 儲存的體系與知識,希望在幫助作者梳理知識體系的同時也能幫助到廣大前端ers!
cookie
由於http是無狀態的協議,一旦客戶端和伺服器的資料交換完畢,就會斷開連線,再次請求,會重新連線,伺服器單從網路連線上無法知道使用者身份。怎麼辦呢?那就給每次新的使用者請求時,給它頒發一個身份證(獨一無二)吧,下次訪問,必須帶上身份證,這樣伺服器就會知道是誰來訪問了,針對不同使用者,做出不同的響應,這就是Cookie的原理。
Cookie 的本職工作並非本地儲存,而是“維持狀態”。它是瀏覽器儲存在使用者機器的一個小文字檔案,大小不能超過4k,並且一些瀏覽器甚至會限制cookie的數量。Cookie是純文字,沒有可執行程式碼。儲存一些伺服器需要的資訊,每次請求站點,會傳送相應的cookie,這些cookie可以用來辨別使用者身份資訊等作用。
cookie的編碼方式為encodeURI()。
cookie型別
cookie按照過期時間分為兩類:會話cookie和持久cookie。
會話cookie是一種臨時cookie,對標session,當使用者退出瀏覽器,會話cookie就會被刪除。預設情況下,即不設定過期時間,設定的cookie為會話cookie。持久cookie則會儲存在硬碟裡,保留時間更長,不以瀏覽器的關閉為轉移,通常是永續性的cookie會維護某一個使用者週期性訪問伺服器的配置檔案或者登入資訊。
cookie的屬性
domain(cookie的域)
產生Cookie的伺服器可以向set-Cookie響應首部新增一個domain屬性來控制哪些站點可以看到那個cookie,例如下面:
Set-Cookie:name="losstie";domain="m.baidu.com"
如果使用者訪問m.baidu.com就會傳送這個cookie,不是則不會。
path(cookie的路徑 )
path 引數告訴瀏覽器 cookie 的路徑。預設情況下,cookie 屬於當前頁面。
secure
設定了屬性secure,cookie只有在https協議加密情況下才會傳送給服務端。但是這並不是最安全的,由於其固有的不安全性,敏感資訊也是不應該通過cookie傳輸的。
HttpOnly
禁止javascript操作cookie(為避免跨域指令碼(xss)攻擊,通過javascript的document.cookie無法訪問帶有HttpOnly標記的cookie。)
SamSite
Cookie 的SameSite
屬性用來限制第三方 Cookie,從而減少安全風險。它可以設定三個值:
- Strict 完全禁止第三方 Cookie,跨站點時,任何情況下都不會傳送 Cookie。換言之,只有當前網頁的 URL 與請求目標一致,才會帶上 Cookie。
- Lax 規則稍稍放寬,大多數情況也是不傳送第三方 Cookie,但是導航到目標網址的 Get 請求除外。導航到目標網址的 GET 請求,只包括三種情況:連結,預載入請求,GET 表單
- None Chrome 計劃將
Lax
變為預設設定。這時,網站可以選擇顯式關閉SameSite
屬性,將其設為None
。不過,前提是必須同時設定Secure
屬性(Cookie 只能通過 HTTPS 協議傳送),否則無效。
cookie操作
通過docuemnt.cookie可以設定和獲取Cookie的值
/* cookie */
function set(key, value){
document.cookie = key + "=" + value;
}
function get(key){
var all = document.cookie;
var s = all.indexOf(key)+key.toString().length+1;
var e = all.indexOf(";",s);
return all.substring(s,e);
}
function remove(key){
var e = "expires=" + new Date(1970,1,1); // 標註過去的時間
document.cookie = key + "=;" + e;
}
第三方cookie
通常cookie的域和瀏覽器地址的域匹配,這被稱為第一方cookie。第三方cookie就是cookie的域和位址列中的域不匹配,這種cookie通常被用在第三方廣告網站。用於跟蹤使用者的瀏覽記錄,並且根據收集的使用者的瀏覽習慣,給使用者推送相關的廣告。
cookie的劣勢
- Cookie 不夠大,體積不能超過4k,當 Cookie 超過 4KB 時,它將面臨被裁切的命運。這樣看來,Cookie 只能用來存取少量的資訊。
- 每次都會攜帶在http頭中,過量的cookie會損耗效能。
- cookie是緊跟域名的,同一個域名下的所有請求,都會攜帶 Cookie。
- 不夠安全,伺服器沒法分辨使用者和攻擊者,攻擊者可以讀取網路上的其他使用者的資訊,包含HTTP Cookie的全部內容,以便進行中間的攻擊。使用跨站點指令碼技術可以竊取cookie等。
Web Storage
Internet Explorer 8+, Firefox, Opera, Chrome, 和 Safari支援Web 儲存。
Web Storage 是 HTML5 專門為瀏覽器儲存而提供的資料儲存機制。它又分為 Local Storage 與 Session Storage。localStorage與sessionStorage儲存的資料,以“鍵值對”的形式存在,並都是以文字格式儲存。
localStorage與sessionStorage的區別
兩者的區別在於生命週期與作用域的不同。
- 生命週期: Local Storage 是持久化的本地儲存,儲存在其中的資料永遠不會過期,只能是手動刪除。Session Storage 是臨時性的本地儲存,它是會話級別的儲存,當會話結束(頁面被關閉)時,儲存內容也隨之被釋放。
- 作用域:Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 在遵循同源策略的前提下,還需要在同一視窗。只要它們不在同一個瀏覽器視窗中開啟,那麼它們的 Session Storage 內容便無法共享。
方法
不管是 localStorage,還是 sessionStorage,可使用的API都相同。以localStorage為例:
- 儲存資料:localStorage.setItem(key,value);
- 讀取資料:localStorage.getItem(key);
- 刪除單個資料:localStorage.removeItem(key);
- 刪除所有資料:localStorage.clear();
- 得到某個索引的key:localStorage.key(index);
module.exports = {
set:set,
get:get,
remove:remove,
removeAll:removeAll,
each:each
}
function set(key ,val){
localStorage.setItem(key, JSON.stringify(val));
};
function get(key){
return JSON.parse(localStorage.getItem(key));
};
function remove(key){
localStorage.removeItem(key);
};
function removeAll(){
localStorage.clear();
};
function each(fn){
pluck(localStorage, function(val, key){
fn(val, key);
return false;
});
}
function pluck(obj, fn){
if(isList(obj)) {
for(var i = 0; i<obj.length;i++){
if(fn(obj[i], i)) {
return obj[i];
}
}
} else {
for(var key in obj) {
if(obj.hasOwnProperty(key)){
if(fn(obj[key], key)){
return obj[key];
}
}
}
}
}
function isList(val) {
return (val != null && typeof val != 'function' && typeof val.length == 'number')
}
function isFunction(val) {
return val && Object.prototype.toString.call(val) === '[object Function]'
}
function isObject(val) {
return val && Object.prototype.toString.call(val) === '[object Object]'
}
Web Storage 特點
- 儲存容量大: Web Storage 根據瀏覽器的不同,儲存容量可以達到 5-10M 之間。Chrome、FireFox、Edge 都是 5M(IE 忽略)。
- 僅位於瀏覽器端,不與服務端發生通訊。
應用場景
localStorage:
- 快取靜態資源,比如圖片內容豐富的電商網站會用它來儲存 Base64 格式的圖片字串或者儲存一些不經常更新的 CSS、JS 等靜態資源。
- 作為前端 DB 的儲存介質
sessionStorage:
- 用來儲存生命週期和它同步的會話級別的資訊。比如儲存使用者輸入的內容,當頁面重新整理的時候可以立刻顯示出重新整理前的內容
IndexedDB
IndexedDB 是一個執行在瀏覽器上的非關係型資料庫。理論上來說,IndexedDB 是沒有儲存上限的(一般來說不會小於 250M)。它不僅可以儲存字串,還可以儲存二進位制資料。
IndexedDB特點
- 鍵值對儲存,IndexedDB 內部採用物件倉庫(object store)存放資料。
- 非同步,ndexedDB 操作時不會鎖死瀏覽器,使用者依然可以進行其他操作(與 LocalStorage 形成對比,後者的操作是同步的)。
- 支援事務,只要有一步失敗,整個事務就都取消,資料庫回滾到事務發生之前的狀態,不存在只改寫一部分資料的情況。
- 同源限制,每一個資料庫對應建立它的域名。網頁只能訪問自身域名下的資料庫,而不能訪問跨域的資料庫。
- 儲存空間大,一般來說不少於 250MB,甚至沒有上限。
- 支援二進位制儲存,僅可以儲存字串,還可以儲存二進位制資料(ArrayBuffer 物件和 Blob 物件)。
常見操作
IndexedDB大部分操作並不是常用的呼叫方法,返回結果的模式,而是請求——響應的模式。
建立開啟IndexedDB ----
window.indexedDB.open("testDB")
關閉IndexedDB----
indexdb.close()
刪除IndexedDB----
indexedDB.deleteDatabase(indexdb)
應用場景
在 IndexedDB 中,我們可以建立多個資料庫,一個資料庫中建立多張表,一張表中儲存多條資料——這足以 hold 住複雜的結構性資料。
- 不需要網路連線的離線純應用,比如Todolist這類用來記錄待辦任務型別的應用。
- 需要儲存大量資料的應用,比如圖書館管理系統這類儲存大量資料的應用;
- 配合service work構建pwa應用,用於快取網路請求。
cookie/webStorage/IndexedDB區別
小結
瀏覽器儲存、快取技術的出現和發展,為前端應用帶來了無限的轉機,頁面越發複雜,功能越發強大。可以說,現代前端應用,尤其是移動端應用,之所以可以發展到在體驗上叫板 Native 的地步,web儲存功不可沒(還有快取)。