前言
HTTP是無狀態的協議,網絡早期最大的問題之一是如何管理狀態。服務器無法知道兩個請求是否來自同一個瀏覽器。cookie應運而生,開始出現在各大網站,然而隨着前端應用復雜度的提高,Cookie 也漸漸演化為了一個“存儲多面手”,承載了 自身僅有的4KB 內存所不能承受的壓力。在這樣的背景下,web Storage應運而生,專門用於瀏覽器存儲。但web Storage也僅僅是cookie的擴展,只能用於存儲少量的簡單數據,當遇到大規模的、結構復雜的數據時,Web Storage 也愛莫能助。這時候,就不得不其強大的IndexedDB了,一個運行在瀏覽器上的非關系型數據庫。本文從cookie講起,Web Storage過渡,IndexedDB結尾,梳理一下web 存儲的體系與知識,希望在幫助作者梳理知識體系的同時也能幫助到廣大前端ers!
cookie
由於http是無狀態的協議,一旦客戶端和服務器的數據交換完畢,就會斷開連接,再次請求,會重新連接,服務器單從網絡連接上無法知道用戶身份。怎么辦呢?那就給每次新的用戶請求時,給它頒發一個身份證(獨一無二)吧,下次訪問,必須帶上身份證,這樣服務器就會知道是誰來訪問了,針對不同用戶,做出不同的響應,這就是Cookie的原理。
Cookie 的本職工作並非本地存儲,而是“維持狀態”。cookie與特定域綁定,是瀏覽器存儲在用戶機器的一個小文本文件,設置cookie后會與請求一起發送到創建它的域。該限制能保證cookie中存儲的信息只對被認可的接收者開放,不被其他域訪問。cookie大小不能超過4k,每個域能夠設置的cookie總數也是有限的,但不同瀏覽器限制不同,如果cookie超過單個域的上限,瀏覽器則會刪除之前設置的cookie,可以使用設置子cookie的方式繞開瀏覽器對每個域cookie數量的限制。Cookie是純文本,沒有可執行代碼。儲存一些服務器需要的信息,每次請求站點,會發送相應的cookie,這些cookie可以用來辨別用戶身份信息等作用。
cookie的編碼方式為encodeURIComponent(),解碼使用decodeURIComponent()。
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存儲功不可沒(還有緩存)。