隨着移動網絡的發展與演化,我們手機上現在除了有原生APP,還能跑WebApp,它即開即用,用完即走。一個優秀的WebApp甚至可以擁有和原生App媲美的功能和體驗。WebApp優異的性能表現,有一部分原因要歸功於瀏覽器存儲技術的提升。Cookie存儲數據的功能已經很難滿足開發所需,逐漸被WebStorage和IndexedDB取代。
Cookie
什么是Cookie
Cookie是指某些網站為了辨別用戶身份而存儲在用戶本地終端上的數據(通常經過加密)。Cookie是由服務器生成,客戶端進行維護和存儲。通過Cookie,可以讓服務器知道請求是來源自哪個客戶端並對客戶端狀態進行維護。比如登錄后的刷新和跳轉,請求頭會攜帶登陸時候Response Header中的set-cookie屬性,Web服務器在接收請求的時候就能讀出Cookie中的值,根據Cookie中的值就可以判斷和恢復一些用戶的信息狀態。
通過上面的描述我們可以知道,Cookie的本職工作並非是本地存儲,而是狀態維持。因為HTTP協議是無狀態的,不會保存請求和響應之間的通信狀態。這就意味着,服務器在這種無狀態交互下,不會知道用戶上一次做了什么,也就阻礙了交互式Web應用程序的實現。Cookie就是為了繞開HTTP的無狀態性而誕生的一種解決方案,服務器可以通過設置和讀取Cookie中包含的用戶信息來維持用戶與服務器會話中的狀態。可以把Cookie理解為一個存儲在瀏覽器中的小體積的文本文件,在請求的時候將攜帶着用戶信息的Cookie附着在HTTP請求上,服務器就可以獲取到客戶端的狀態。
Cookie的典型應用場景有自動登錄(記住密碼)、購物車功能、記錄用戶行為(做商品/廣告推薦)等。
Cookie的原理與生成方式
舉個簡單的例子,在典型的網上購物場景中,用戶瀏覽了幾個頁面,買了一盒餅干和兩瓶飲料,最后結賬的時候,由於HTTP的無狀態性,服務器根本不能知道用戶到底買了什么,或者說用戶和用戶購買的商品無法對應上。而有了Cookie之后,當用戶成功登錄之后,服務器在返回成功登錄響應的時候就會給客戶端返回一個攜帶用戶信息的Cookie,客戶端會保存這個Cookie。當用戶選購了商品並發送請求,請求中就會帶上這個Cookie,然后服務器就可以檢查Cookie並獲取Cookie中的用戶信息,讓用戶和購買的商品能夠對應得上。
第一次訪問網站的時候,瀏覽器發出請求,服務器響應請求后,會在響應頭里面添加一個set-cookie選項,將Cookie放入到響應請求中,在瀏覽器第二次發請求的時候,會通過Cookie請求頭部將Cookie信息發送給服務器,服務端會辨別用戶身份,另外,Cookie的過期時間、域、路徑、有效期、適用站點都可以根據需要來指定。
Cookie的生成方式主要有以下兩種:
1.http response header中的set-cookie。
2.js中通過document.cookie讀寫cookie,可以將cookie解析為鍵值對的形式展示。
Cookie的缺陷
1.Cookie不夠大。Cookie的大小限制在4KB左右,對於復雜的存儲需求來說是不夠用的。當Cookie超過4KB的時候,它將會面臨被裁切的命運。這樣看來,Cookie只能用來存取少量的信息,甚至有些瀏覽器對一個站點的Cookie個數都有做限制。這里要注意的是,這里的4KB容量限制是對Cookie中name=value的value來說的,並不是一個域名下所有的Cookie共享的。
2.過多的Cookie會帶來巨大的性能/資源浪費。Cookie是緊跟域名的,同一個域名下的所有請求都會攜帶Cookie。很多場景其實並不需要身份驗證,比如請求一張圖片或者CSS文件,這種時候攜帶Cookie也就造成了性能/資源的浪費。有個解決方案就是使用CDN作為靜態資源文件的請求服務方式,因為CDN的域名與主站的域名是分開的,也就不會在請求中附帶Cookie了。
Cookie的安全性
雖然Cookie中有些屬性可以適當維持安全性,但是其不安全性幾乎仍然是固有的。
為了彌補Cookie的局限性,WebStorage就產生了。WebStorage是HTML5中新增的本地存儲解決方案,包含了SessionStorage和LocalStorage兩類。有了WebStorage之后,Cookie就能夠安心只作為客戶端與服務器交互的通道(保持客戶端狀態)了,不用再操心存儲的事情。
LocalStorage
LocalStorage是WebStorage之一。
LocalStorage的特點
1.保存的數據長期存在,下一次訪問該網站的時候,網頁可以直接讀取之前保存的數據。
2.存儲限制大小為5MB左右。
3.僅在客戶端使用,不和服務端進行通信。
4.接口封裝得較好。
基於上面的特點,LocalStorage可以作為瀏覽器的本地緩存方案,用來提升網頁首屏渲染速度(根據第一請求返回時,將一些不變的信息直接存儲在本地)。
LocalStorage存入/讀取數據
LocalStorage保存的數據是以鍵值對的形式存在的,也就是說,每一項數據都有一個鍵名和對應的值,且所有的數據都是以文本格式保存的。
存入數據使用setItem()方法。這個方法接受兩個參數,第一個是鍵名,第二個是保存的數據。
localStorage.setItem(key, value);
讀取數據使用getItem()方法。這個方法接受一個參數,就是鍵名。
localStorage.getItem(key);
來看一個完整的例子:
function testLocalStorage() { if (window.localStorage) { localStorage.setItem('name', 'yanggb'); console.log(localStorage.getItem('name')); } }
LocalStorage的使用場景
LocalStorage在存儲方面幾乎沒有什么特別的限制,在理論上,一些Cookie無法勝任的、存儲簡單鍵值對的存儲任務都可以交給LocalStorage來做。
考慮到LocalStorage的特點之一是持久,因此有時候我們更傾向於用它來存儲一些內容穩定的資源。比如圖片內容豐富的電商網站可以使用LocalStorage來存儲Base64格式的圖片字符串。
SessionStorage
SessionStorage保存的數據用於瀏覽器的一次會話,當會話結束(通常是該窗口關閉),數據就會被清空。SessionStorage特別的一點在於,即便是相同域名下的兩個頁面,只要它們不在同一個瀏覽器窗口中打開,那么它們的SessionStorage內容便無法共享,而LocalStorage在所有同源窗口中共享的,Cookie也是在所有同源窗口中共享的。除了保存期限的長短不同,SessionStorage的屬性和方法與LocalStorage完全一樣。
SessionStorage的特點
1.會話級別的瀏覽器存儲。
2.存儲大小限制為5MB左右。
3.僅在客戶端使用,不與服務端進行通信。
4.接口封裝得較好。
基於上面的特點,SessionStorage可以有效地對表單信息進行緩存,比如刷新時表單的信息不會丟失。
SessionStorage的使用場景
SessionStorage更適合用來存儲生命周期和它同步的會話級別的信息,這些信息只適用於當前的會話。當你開啟新的會話時,它也需要相應的更新或釋放。比如微博就使用SessionStorage存儲單次會話的瀏覽足跡,它會存一個lasturl鍵去對應你上一次訪問的url地址。當你切換url的時候,這個鍵值就會更新;當你關閉頁面的時候,這個鍵就會被釋放。這樣的數據用SessionStorage來處理再合適不過了。
SessionStorage、LocalStorage和Cookie之間的區別
SessionStorage、LocalStorage和Cookie都是存儲在瀏覽器端,都遵循瀏覽器的同源策略,不同的地方在於它們的生命周期和作用域不同。
作用域的不同
LocalStorage只要在相同的協議、相同的主機名、相同的端口下,就能讀取/修改到同一份LocalStorage數據。而SessionStorage則比LocalStorage要更嚴苛一點,除了協議、主機名、端口外,還要求在同一窗口(也就是瀏覽器的標簽頁)下。
生命周期的不同
LocalStorage是持久化的本地存儲,存儲在其中的數據是永遠不會過期的,使其消失的唯一辦法是手動刪除;而SessionStorage是臨時性的本地存儲,是會話級別的存儲,當會話結束(頁面被關閉)時,存儲內容也會隨之被釋放。
Web Storage是一個從定義到使用都非常簡單的東西。它使用鍵值對的形式進行存儲,這種模式有點類似於對象,可是鍵值卻只能存儲字符串。如果想要得到對象,還需要先對字符串進行一輪解析。因此Web Storage可以看作是對Cookie的拓展,只能用於存儲少量的簡單數據。當遇到大規模的、結構復雜的數據時,就需要用到IndexedDB。
IndexedDB
IndexedDB是一種低級的API,使用索引來實現對其間存儲數據的高性能搜索,用於客戶端需要存儲大量結構化數據(包括文件和blobs)的場景。
IndexedDB可以看作是一個運行在瀏覽器上的非關系型數據庫。既然是數據庫了,那就不是5M、10M這樣小打小鬧的級別了,理論上來說,IndexedDB是沒有存儲上限的,一般來說不會小於250M。另外,它不僅可以存儲字符串,還可以存儲二進制數據。
IndexedDB的特點
1.鍵值對存儲。IndexedDB內部采用對象倉庫(Object Store)存放數據,所有類型的數據都可以直接存入,包括JavaScript對象。在對象倉庫中,數據以鍵值對的形式保存,每一個數據記錄都有對應的主鍵,且主鍵是唯一的,不可重復,否則會拋出錯誤。
2.異步。IndexedDB在操作時不會鎖死瀏覽器(異步線程,不會掛起其他線程),用戶依然可以可以進行其他操作。這與LocalStorage形成對比,后者的操作是同步的(會掛起其他線程)。異步設計是為了防止讀寫大量數據的時候拖慢網頁的表現性能。
3.支持事務。IndexedDB是支持事務(Transaction)的,也同樣支持事務的ACID特性。這意味着在一系列的操作步驟中,只要有一步失敗,整個事務就會取消,數據庫將回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況,保證數據的完整性和一致性。
4.同源限制。IndexedDB受瀏覽器同源策略的限制,每一個數據庫都嚴格對應創建它的域名。網頁只能訪問自身域名下面的數據庫,而不能訪問跨域的數據庫。
5.存儲空間大。IndexedDB的存儲空間比LocalStorage大得多,一般來說不小於250MB,甚至沒有上限。
6.支持二進制存儲。IndexedDB不僅可以存儲字符串,還可以存儲二進制數據(ArrayBuffer對象和Blob對象)。
IndexedDB的創建、關閉和刪除操作
在IndexedDB的大部分操作並不是我們常用的調用方法(返回結果的模式),而是請求-響應的模式。
1.建立打開IndexedDB。
window.indexedDB.open("testdb");
上面這條語句並不會返回一個DB對象的句柄,我們得到的是一個IDBOpenDBRequest對象,在這個對象中的result屬性中會包含我們想要的DB對象。
除了result之外,IDBOpenDBRequest接口還定義了幾個重要的屬性。
屬性 | 值 |
onerror | 請求失敗的回調函數句柄 |
onsuccess | 請求成功的回調函數句柄 |
onupgradeneeded | 請求數據庫版本變化句柄 |
來看一個完整的例子:
// 全局myDB對象 var myDB = { name: 'testdb', db: null } // 定義創建打開indexedDB的函數 function openDB (dbName) { var request = window.indexedDB.open(dbName); // 創建打開失敗事件 request.onerror = function(e) { console.log('open indexdb error'); } // 創建打開成功事件 request.onsuccess = function(e) { myDB.db = e.target.result; console.log(myDB.db); } } // 調用創建打開函數 openDB(myDB.name);
2.關閉IndexedDB。
// 定義關閉IndexedDB函數 function closeDB() { myDB.db.close(); }
這里要注意調用close()方法的是IndexedDB對象,且關閉並不會刪除瀏覽器中的IndexedDB數據庫。
3.刪除IndexedDB。
// 定義刪除IndexedDB的函數 function deleteDB() { window.indexedDB.deleteDatabase(myDB.name); }
要特別提及的是,IndexedDB的命名是嚴格區分大小寫的,無論是創建還是刪除都要注意這一點。
關於IndexedDB的使用,內容比較多,另外開篇去深入了解。
Cookie、WebStorage和IndexedDB之間的區別
從上表中可以看出,Cookie已經不建議用於存儲用途。如果沒有大量數據存儲需求的話,可以使用LocalStorage和SessionStorage。對於不怎么改變的數據盡量使用LocalStorage做存儲,否則可以用SessionStorage做存儲。
瀏覽器存儲的總結
因為瀏覽器存儲和緩存技術的出現和發展,前端應用帶來了無限的可能。近年來,基於存儲、緩存技術的第三方庫層出不窮,此外還衍生出了PWA這樣優秀的Web應用模型。
1.Cookie的本質工作並不是本地存儲,而是狀態維持。
2.WebStorage是HTML5專門為瀏覽器存儲而提供的數據存儲機制,不與服務端發生通信。
3.IndexedDB用於客戶端存儲大量結構化的數據。
"你一邊討厭着你現在的生活狀態,一邊懶散地混過每一天。"