網絡早期最大的問題之一是如何管理狀態。簡而言之,服務器沒法知道兩個請求是否來自同一個瀏覽器。當時最簡單的方法是在請求時,在頁面中插入一些參數,並在下一個請求中傳回參數。這須要使用包含參數的隱藏的表單,或者做爲URL參數的一部分傳遞。這兩個解決方案都手動操做,容易出錯。cookie出現來解決這個問題。javascript
cookie是純文本,沒有可執行代碼。存儲數據,當用戶訪問了某個網站(網頁)的時候,咱們就能夠經過cookie來向訪問者電腦上存儲數據,或者某些網站爲了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據(一般通過加密)css
當網頁要發http請求時,瀏覽器會先檢查是否有相應的cookie,有則自動添加在request header中的cookie字段中。這些是瀏覽器自動幫咱們作的,並且每一次http請求瀏覽器都會自動幫咱們作。這個特色很重要,由於這關係到「什麼樣的數據適合存儲在cookie中」。html
存儲在cookie中的數據,每次都會被瀏覽器自動放在http請求中,若是這些數據並非每一個請求都須要發給服務端的數據,瀏覽器這設置自動處理無疑增長了網絡開銷;但若是這些數據是每一個請求都須要發給服務端的數據(好比身份認證信息),瀏覽器這設置自動處理就大大免去了重複添加操做。因此對於那種設置「每次請求都要攜帶的信息(最典型的就是身份認證信息)」就特別適合放在cookie中,其餘類型的數據就不適合了。前端
客戶端設置java
document.cookie = '名字=值'; document.cookie = 'username=cfangxu;domain=baike.baidu.com' 而且設置了生效域
注意: 客戶端能夠設置cookie 的下列選項:expires、domain、path、secure(有條件:只有在https協議的網頁中,客戶端設置secure類型的 cookie 才能成功),但沒法設置HttpOnly選項。web
服務器端設置
無論你是請求一個資源文件(如 html/js/css/圖片),仍是發送一個ajax請求,服務端都會返回response。而response header中有一項叫set-cookie,是服務端專門用來設置cookie的。ajax
Set-Cookie 消息頭是一個字符串,其格式以下(中括號中的部分是可選的):
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
注意: 一個set-Cookie字段只能設置一個cookie,當你要想設置多個 cookie,須要添加一樣多的set-Cookie字段。
服務端能夠設置cookie 的全部選項:expires、domain、path、secure、HttpOnly
經過 Set-Cookie 指定的這些可選項只會在瀏覽器端使用,而不會被髮送至服務器端。數據庫
咱們經過document.cookie來獲取當前網站下的cookie的時候,獲得的字符串形式的值,它包含了當前網站下全部的cookie(爲避免跨域腳本(xss)攻擊,這個方法只能獲取非 HttpOnly 類型的cookie)。它會把全部的cookie經過一個分號+空格的形式串聯起來,例如username=chenfangxu; job=coding
後端
要想修改一個cookie,只須要從新賦值就行,舊的值會被新的值覆蓋。但要注意一點,在設置新cookie時,path/domain這幾個選項必定要舊cookie 保持同樣。不然不會修改舊值,而是添加了一個新的 cookie。跨域
把要刪除的cookie的過時時間設置成已過去的時間,path/domain/這幾個選項必定要舊cookie 保持同樣。
若是隻設置一個值,那麼算cookie中的value; 設置的兩個cookie,key值若是設置的相同,下面的也會把上面的覆蓋。
若是咱們想長時間存放一個cookie。須要在設置這個cookie的時候同時給他設置一個過時的時間。若是不設置,cookie默認是臨時存儲的,當瀏覽器關閉進程的時候自動銷燬
注意:document.cookie = '名稱=值;expires=' + GMT(格林威治時間)格式的日期型字符串;
通常設置天數:new Date().setDate( oDate.getDate() + 5 ); 比當前時間多5天
一個設置cookie時效性的例子
function setCookie(c_name, value, expiredays){ var exdate=new Date(); exdate.setDate(exdate.getDate() + expiredays); document.cookie=c_name+ "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate.toGMTString()) } 使用方法:setCookie('username','cfangxu',30)
expires 是 http/1.0協議中的選項,在新的http/1.1協議中expires已經由 max-age 選項代替,二者的做用都是限制cookie 的有效時間。expires的值是一個時間點(cookie失效時刻= expires),而max-age 的值是一個以秒爲單位時間段(cookie失效時刻= 建立時刻+ max-age)。
另外,max-age 的默認值是 -1(即有效期爲 session );max-age有三種可能值:負數、0、正數。
負數:有效期session;
0:刪除cookie;
正數:有效期爲建立時刻+ max-age
domain指定了 cookie 將要被髮送至哪一個或哪些域中。默認狀況下,domain 會被設置爲建立該 cookie 的頁面所在的域名,因此當給相同域名發送請求時該 cookie 會被髮送至服務器。
瀏覽器會把 domain 的值與請求的域名作一個尾部比較(即從字符串的尾部開始比較),並將匹配的 cookie 發送至服務器。
document.cookie = "username=cfangxu;path=/;domain=qq.com"
如上:「www.qq.com" 與 "sports.qq.com" 公用一個關聯的域名"qq.com",咱們若是想讓 "sports.qq.com" 下的cookie被 "www.qq.com" 訪問,咱們就須要用到 cookie 的domain屬性,而且須要把path屬性設置爲 "/"。
Set-Cookie: username=cfangxu;path=/;domain=qq.com
注:必定的是同域之間的訪問,不能把domain的值設置成非主域的域名。
cookie 通常都是因爲用戶訪問頁面而被建立的,但是並非只有在建立 cookie 的頁面才能夠訪問這個 cookie。
由於安全方面的考慮,默認狀況下,只有與建立 cookie 的頁面在同一個目錄或子目錄下的網頁才能夠訪問。
即path屬性能夠爲服務器特定文檔指定cookie,這個屬性設置的url且帶有這個前綴的url路徑都是有效的。
最經常使用的例子就是讓 cookie 在根目錄下,這樣無論是哪一個子頁面建立的 cookie,全部的頁面均可以訪問到了。
document.cookie = "username=cfangxu; path=/"
Set-Cookie:name=cfangxu; path=/blog
如上設置:path 選項值會與 /blog,/blogrool 等等相匹配;任何以 /blog 開頭的選項都是合法的。須要注意的是,只有在 domain 選項覈實完畢以後纔會對 path 屬性進行比較。path 屬性的默認值是發送 Set-Cookie 消息頭所對應的 URL 中的 path 部分。
domain是域名,path是路徑,二者加起來就構成了 URL,domain和path一塊兒來限制 cookie 能被哪些 URL 訪問。
因此domain和path2個選項共同決定了cookie什麼時候被瀏覽器自動添加到請求頭部中發送出去。若是沒有設置這兩個選項,則會使用默認值。domain的默認值爲設置該cookie的網頁所在的域名,path默認值爲設置該cookie的網頁所在的目錄。
一般 cookie 信息都是使用HTTP鏈接傳遞數據,這種傳遞方式很容易被查看,因此 cookie 存儲的信息容易被竊取。假如 cookie 中所傳遞的內容比較重要,那麼就要求使用加密的數據傳輸。
secure選項用來設置cookie只在確保安全的請求中才會發送。當請求是HTTPS或者其餘安全協議時,包含 secure 選項的 cookie 才能被髮送至服務器。
document.cookie = "username=cfangxu; secure"
把cookie設置爲secure,只保證 cookie 與服務器之間的數據傳輸過程加密,而保存在本地的 cookie文件並不加密。就算設置了secure 屬性也並不表明他人不能看到你機器本地保存的 cookie 信息。機密且敏感的信息毫不應該在 cookie 中存儲或傳輸,由於 cookie 的整個機制本來都是不安全的
注意:若是想在客戶端即網頁中經過 js 去設置secure類型的 cookie,必須保證網頁是https協議的。在http協議的網頁中是沒法設置secure類型cookie的。
這個選項用來設置cookie是否能經過 js 去訪問。默認狀況下,cookie不會帶httpOnly選項(即爲空),因此默認狀況下,客戶端是能夠經過js代碼去訪問(包括讀取、修改、刪除等)這個cookie的。當cookie帶httpOnly選項時,客戶端則沒法經過js代碼去訪問(包括讀取、修改、刪除等)這個cookie。
在客戶端是不能經過js代碼去設置一個httpOnly類型的cookie的,這種類型的cookie只能經過服務端來設置。
cookie實際上是個字符串,但這個字符串中等號、分號、空格被當作了特殊符號。因此當cookie的 key 和 value 中含有這3個特殊字符時,須要對其進行額外編碼,通常會用escape進行編碼,讀取時用unescape進行解碼;固然也能夠用encodeURIComponent/decodeURIComponent或者encodeURI/decodeURI
一般cookie的域和瀏覽器地址的域匹配,這被稱爲第一方cookie。那麼第三方cookie就是cookie的域和地址欄中的域不匹配,這種cookie一般被用在第三方廣告網站。爲了跟蹤用戶的瀏覽記錄,而且根據收集的用戶的瀏覽習慣,給用戶推送相關的廣告。
cookie推薦資源
HTML5新方法,不過IE8及以上瀏覽器都兼容。
localStorage.setItem('username','cfangxu');
localStorage.getItem('username')
也能夠獲取鍵名 localStorage.key(0) #獲取第一個鍵名
localStorage.removeItem('username')
也能夠一次性清除全部存儲 localStorage.clear()
當storage發生改變的時候觸發。
注意: 當前頁面對storage的操做會觸發其餘頁面的storage事件
事件的回調函數中有一個參數event,是一個StorageEvent對象,提供了一些實用的屬性,以下表:
Property | Type | Description |
---|---|---|
key | String | The named key that was added, removed, or moddified |
oldValue | Any | The previous value(now overwritten), or null if a new item was added |
newValue | Any | The new value, or null if an item was added |
url/uri | String | The page that called the method that triggered this change |
其實跟localStorage差很少,也是本地存儲,會話本地存儲
localStorage、sessionStorage
localStorage只要在相同的協議、相同的主機名、相同的端口下,就能讀取/修改到同一份localStorage數據。
sessionStorage比localStorage更嚴苛一點,除了協議、主機名、端口外,還要求在同一窗口(也就是瀏覽器的標籤頁)下。
localStorage是永久存儲,除非手動刪除。
sessionStorage當會話結束(當前頁面關閉的時候,自動銷燬)
cookie的數據會在每一次發送http請求的時候,同時發送給服務器而localStorage、sessionStorage不會。
先說個會被取代的,爲何會被取代,主要有如下幾個緣由:
Web SQL database
草案,並且是在2010年年末,規範不支持了,瀏覽器廠商已經支持的就支持了,沒有支持的也不打算支持了,好比IE和Firefox。Web SQL database
本質上是一個關係型數據庫,後端可能熟悉,可是前端就有不少不熟悉了,雖然SQL的簡單操做不難,可是也得須要學習。來自MDN的解釋: indexedDB 是一種低級API,用於客戶端存儲大量結構化數據(包括, 文件/ blobs)。該API使用索引來實現對該數據的高性能搜索。雖然 Web Storage 對於存儲較少許的數據頗有用,但對於存儲更大量的結構化數據來講,這種方法不太有用。IndexedDB提供了一個解決方案。
因此,IndexedDB
API是強大的,但對於簡單的狀況可能看起來太複雜了,因此要看你的業務場景來選擇究竟是用仍是不用。
indexedDB
是一個基於JavaScript的面向對象的數據庫。 IndexedDB
容許你存儲和檢索用鍵索引的對象;
IndexedDB 鼓勵使用的基本模式以下所示:
語法: window.indexedDB.open(dbName, version)
var db; // 打開數據庫,open還有第二個參數版本號 var request = window.indexedDB.open('myTestDatabase'); // 數據庫打開成功後 request.onsuccess = function (event) { // 存儲數據結果,後面全部的數據庫操做都離不開它。 db = request.result; } request.onerror = function (event) { alert("Why didn't you allow my web app to use IndexedDB?!"); } // 數據庫首次建立版本,或者window.indexedDB.open傳遞的新版本(版本數值要比如今的高) request.onupgradeneeded = function (event) { }
onupgradeneeded事件: 更新數據庫的 schema,也就是建立或者刪除對象存儲空間,這個事件將會做爲一個容許你處理對象存儲空間的 versionchange
事務的一部分被調用。在數據庫第一次被打開時或者當指定的版本號高於當前被持久化的數據庫的版本號時,這個 versionchange
事務將被建立。onupgradeneeded
是咱們惟一能夠修改數據庫結構的地方。在這裏面,咱們能夠建立和刪除對象存儲空間以及構建和刪除索引。
IndexedDB 使用對象存儲空間而不是表,而且一個單獨的數據庫能夠包含任意數量的對象存儲空間。每當一個值被存儲進一個對象存儲空間時,它會被和一個鍵相關聯。
// 數據庫首次建立版本,或者window.indexedDB.open傳遞的新版本(版本數值要比如今的高) request.onupgradeneeded = function (event) { //以前我們不是在success中獲得了db了麼,爲何還要在這獲取, //由於在當前事件函數執行後纔會去執行success事件 var db = event.target.result; // 建立一個對象存儲空間,keyPath是id,keyGenerator是自增的 var objectStore = db.createObjectStore('testItem',{keyPath: 'id',autoIncrement: true}); // 建立一個索引來經過id搜索,id是自增的,不會有重複,因此能夠用惟一索引 objectStore.createIndex('id','id',{unique: true}) objectStore.createIndex('name','name'); objectStore.createIndex('age','age'); //添加一條信息道數據庫中 objectStore.add({name: 'cfangxu', age: '27'}); }
注意: 執行完後,在調試工具欄Application的indexedDB中也看不到,你得右鍵刷新一下。
建立索引的語法:
objectStore.createIndex(indexName, keyPath, objectParameters)
indexName:建立的索引名稱,可使用空名稱做爲索引。 keyPath:索引使用的關鍵路徑,可使用空的keyPath, 或者keyPath傳爲數組keyPath也是能夠的。 objectParameters:可選參數。經常使用參數之一是unique,表示該字段值是否惟一,不能重複。例如,本demo中id是不能重複的,因而有設置:
上面的代碼建好了字段,而且添加了一條數據,可是咱們若是想在onupgradeneeded事件外面操做,接下來的步驟了。
因爲數據庫的操做都是基於事務(transaction)來進行,因而,不管是添加編輯仍是刪除數據庫,咱們都要先創建一個事務(transaction),而後才能繼續下面的操做。
語法: var transaction = db.transaction(dbName, "readwrite");
第一個參數是事務但願跨越的對象存儲空間的列表,能夠是數組或者字符串。若是你但願事務可以跨越全部的對象存儲空間你能夠傳入一個空數組。若是你沒有爲第二個參數指定任何內容,你獲得的是隻讀事務。由於這裏咱們是想要寫入因此咱們須要傳入 "readwrite" 標識。
var timer = setInterval(function () { if(db) { clearInterval(timer); // 新建一個事務 var transaction = db.transaction(['testItem'], 'readwrite'); // 打開一個存儲對象 var objectStore = transaction.objectStore('testItem'); // 添加數據到對象中 objectStore.add({ name: 'xiaoming', age: '12' }); objectStore.add({ name: 'xiaolong', age: '20' }); } },100)
爲何要用一個間隔定時器? 由於這是一個demo,正常的是要有操做才能進行數據庫的寫入,在咱們的demo中,js執行到transaction會比indexedDB的onsuccess事件回調快,致使會拿到db爲undefined,因此寫了個間隔定時器等它一會。
var transaction = db.transaction(['testItem'], 'readwrite'); var objectStore = transaction.objectStore('testItem'); var getRquest = objectStore.get(1); getRquest.onsuccess = function (event) { console.log(getRquest.result); } //輸出:{name: "cfangxu", age: "27", id: 1}
var transaction = db.transaction(['testItem'], 'readwrite'); var objectStore = transaction.objectStore('testItem'); var getRquest = objectStore.put({ name: 'chenfangxu', age: '27', id:1 }); // 修改了id爲1的那條數據
var transaction = db.transaction(['testItem'], 'readwrite'); var objectStore = transaction.objectStore('testItem'); var getRquest = objectStore.delete(1); // 刪除了id爲1的那條數據