概述
本文主要講述 Session + Storage + Cache-Control + ETag + Cookie 這五者的作用及區(qū)別
Session
首先通過代碼認(rèn)識一下 Session。之前我們說 Cookie 可以存儲(chǔ)我們的一些信息,但是由于用戶在瀏覽器中可以對 Cookie 進(jìn)行操作,顯然這不是我們想要的,所以 Session 應(yīng)運(yùn)而生,Session 解決了 Cookie 不安全的痛點(diǎn)
code
我們在內(nèi)存中開辟一個(gè)空間,用來存儲(chǔ) Session
let sessions = {}
當(dāng)用戶登錄成功時(shí)
let sessionId = Math.random() * 1000000
sessions[ sessionId ] = { key: value } // 表示存儲(chǔ)的用戶信息
response.setHeader( 'Set-Cookie', `sessionId = ${ sessionId }` ) // Cookie: 'sessionId = 隨機(jī)數(shù)'
當(dāng)此用戶訪問首頁時(shí),遍歷 Cookie,將所有 Cookie 存儲(chǔ)到一個(gè) hash(哈希表)中,之后
let mySession = sessions[ hash.sessionId ]
let username
if( mySession ){
username = mySession.用戶信息 // 用戶信息表示 sessions 中的{ key: value }
}
Session 特點(diǎn)
- 服務(wù)器通過 Cookie(
sessionId = ${ sessionId }
) 將 SessionId(隨機(jī)數(shù))發(fā)給瀏覽器 - 服務(wù)器有一塊內(nèi)存保存了所有的 Session(哈希表)
- 當(dāng)瀏覽器訪問服務(wù)器時(shí),服務(wù)器讀取 SessionId
- 服務(wù)器通過 SessionId 可以得到對應(yīng)用戶的隱私信息
- 用戶每次登錄都會(huì)設(shè)置一個(gè) SessionId,并且 SessionId 不保存在服務(wù)器中
Storage
作為 Web Storage API 的接口(HTML5),Storage 提供了訪問特定域名下的會(huì)話存儲(chǔ)(session storage)和本地存儲(chǔ)(local storage)的功能,例如:增刪改查存儲(chǔ)的數(shù)據(jù)項(xiàng)。Storage 與 HTTP 無關(guān),它是瀏覽器上的哈希表,Storage 文件存儲(chǔ)在本地的一個(gè)文件夾中
-
window.sessionStorage
==> 操作一個(gè)域名的會(huì)話存儲(chǔ)(session storage) -
window.localStorage
==> 操作一個(gè)域名的本地存儲(chǔ)(local storage)
API
Storage.setItem()
==> 接收一個(gè)鍵名和值作為參數(shù),把鍵值對添加到存儲(chǔ)中,如果鍵名存在,則更新其對應(yīng)的值Storage.getItem()
==> 接收一個(gè)鍵名作為參數(shù),返回鍵名對應(yīng)的值Storage.removeItem()
==> 接收一個(gè)鍵名作為參數(shù),并把該鍵名從存儲(chǔ)中刪除Storage.clear()
==> 清空存儲(chǔ)中的所有鍵名
對象的存儲(chǔ)
localStorage.setItem( 'object', { name: 'obj' } ) // object [ object Object ]
瀏覽器會(huì)將
{ name: 'obj' }
轉(zhuǎn)化為字符串即 ({ name: 'obj' }).toString
,所以當(dāng)我們存儲(chǔ)對象時(shí),使用 JSON ,即
localStorage.setItem( 'object', JSON.stringify({ name: 'obj' }))
localStorage
使用場景
記錄是否提示過用戶 + 記錄一些不敏感的信息,常見新手引導(dǎo)界面
let already = localStorage.getItem( 'isGuide' )
if( !already ){
// 開啟引導(dǎo)
localStorage.setItem( 'isGuide', true )
}
特點(diǎn)
- localStorage 與 HTTP 無關(guān),所以 HTTP 不會(huì)帶上 localStorage 的值
- 每個(gè)域名的 localStorage 有最大存儲(chǔ)量,因?yàn)g覽器而異
- 只有相同域名的頁面才能互相讀取 localStorage
- localStorage 永久有效,除非用戶清除
sessionStorage
特點(diǎn)
- sessionStorage 與 HTTP 無關(guān),所以 HTTP 不會(huì)帶上 sessionStorage 的值
- 每個(gè)域名的 sessionStorage 有最大存儲(chǔ)量,因?yàn)g覽器而異
- SessionStorage 只在同一瀏覽器窗口中共享
- sessionStorage 在用戶關(guān)閉頁面后就會(huì)失效
Cache-Control
Cache-Control 通用消息頭被用于在 HTTP 請求和響應(yīng)中通過指定指令來實(shí)現(xiàn)緩存機(jī)制。當(dāng)我們請求的文件(css、js)很大時(shí),可以使用 Cache-Control 實(shí)現(xiàn)緩存,從而達(dá)到性能優(yōu)化的目的
前提:使用相同的 URL 才能實(shí)現(xiàn) Cache-Control 緩存機(jī)制
HTML
<link rel = "stylesheet" href = "URL">
<script src = "URL">
后端 + code
else if( path === '/js/main.js' ){
response.setHeader( 'Cache-Control', 'max-age = 30' )
// 30s 內(nèi)如果請求 main.js 文件,瀏覽器不發(fā)送請求,直接使用緩存中文件 ==> 下載時(shí)間 === 0
}
特點(diǎn)
- 讓瀏覽器在一段時(shí)間內(nèi)不訪問服務(wù)器,不發(fā)送請求,直接使用本地硬盤 | 內(nèi)存作為響應(yīng),從而減少請求時(shí)間
- 首頁(入口文件 + HTML)不設(shè)置 Cache-Control,因?yàn)樵诰彺娴倪@段時(shí)間內(nèi),用戶不能獲取最新網(wǎng)頁
- 其他文件(css + js)會(huì)緩存很久(10年,甚至更久),如要更新,只需要改變?nèi)肟谖募℉TML)的 URL 即可,之后瀏覽器就會(huì)緩存最新版的文件
-
URL 改變實(shí)現(xiàn)方式:+ 查詢參數(shù) | + 隨機(jī)數(shù)
URL 改變實(shí)現(xiàn) + 隨機(jī)數(shù)
Expires
Expires 頭指定了一個(gè)日期 | 時(shí)間, 在這個(gè)日期 | 時(shí)間之后,HTTP響應(yīng)被認(rèn)為是過時(shí)的
Cache-Control | Expires
從 Expires ==> Cache-Control 是 HTTP 升級的過程,以前使用 Expires 加緩存,現(xiàn)在使用 Cache-Control 加緩存,Expires 的問題在于,它的過期時(shí)間是本地的時(shí)間,如果本地時(shí)間錯(cuò)亂,可能導(dǎo)致用戶一直不能使用緩存,從而影響用戶體驗(yàn)
兩者的區(qū)別在于:Cache-Control 設(shè)置緩存時(shí)長,Expires 設(shè)置緩存過期時(shí)間點(diǎn)。如果兩者同時(shí)設(shè)置,Cache-Control 優(yōu)先使用
ETag
ETag HTTP 響應(yīng)頭是資源的特定版本的標(biāo)識符??梢宰尵彺娓痈咝Р⒐?jié)省寬帶,如果內(nèi)容沒有改變,Web 服務(wù)器不需要發(fā)送完整的響應(yīng)
MD5
MD5 指摘要算法,它可以把一個(gè)文件轉(zhuǎn)化成一個(gè)字符串。若文件內(nèi)容相同,則字符串相同。文件內(nèi)容差異越小,字符串(算出來的結(jié)果)差異越大
后端 + code
安裝 MD5 npm install md5
,然后 node.js 使用 MD5
else if( path === '/js/main.js' ){
let string = fs.readFileSync( './js/main.js', 'utf-8' )
let fileMd5 = md5( string )
response.setHeader( 'ETag', fileMd5 ) // 響應(yīng)頭中有 ETag ==> ETag: md5 值
// 當(dāng)設(shè)置了 ETag 響應(yīng)頭,下次刷新時(shí),請求中會(huì)多一個(gè) If-None-Match 的請求頭,值為 ETag 的值(md5 值)
if( request.header[ 'if-none-match' ] === fileMd5 ){ // 如果請求的版本號(md5 值) === 瀏覽器的 If-None-Match 的值(md5 值) ==> 相同版本不需要下載
// 沒有響應(yīng)體
response.statusCode = 304
// 304 Not Modified 表示資源未被修改,因?yàn)檎埱箢^指定的版本If-Modified-Since或If-None-Match。在這種情況下,由于客戶端仍然具有以前下載的副本,因此不需要重新傳輸資源。
} else{
response.statusCode = 200
// 有響應(yīng)體
response.write( string )
}
response.end()
}
緩存機(jī)制
Cache-Control + ETag 聯(lián)合使用
辨析
Cookie + Session
- Cookie 指某些服務(wù)器在瀏覽器終端的一些數(shù)據(jù)(通常經(jīng)過加密),一般為了辨別用戶身份,也可以儲(chǔ)存少量信息
- Session 是指服務(wù)器通過某種方式確定了用戶身份后的會(huì)話狀態(tài),一般表現(xiàn)為服務(wù)器為每個(gè)用戶單獨(dú)存儲(chǔ)的一部分?jǐn)?shù)據(jù)
- Session 是基于 Cookie 實(shí)現(xiàn)的,Cookie 是 Session 的基石
- Cookie 存儲(chǔ)在瀏覽器本地,用戶可以看到內(nèi)容。Session 存儲(chǔ)在服務(wù)器,用戶無法查看內(nèi)容,一般 Session 的內(nèi)容是進(jìn)程\線程間共享的
- Cookie 不安全,而 Session 解決了 Cookie 不安全的痛點(diǎn)
Cookie + Storage
- Cookie 和 Storage 都存儲(chǔ)在本地的一個(gè)文件中
- 兩者都可以做跨頁面通信,兩者都不能跨域訪問
- Cookie 的每次請求相同域名時(shí),都會(huì)帶上 Cookie 里的所有內(nèi)容去訪問服務(wù)器
- Storage 與 HTTP 無關(guān),不會(huì)被帶給服務(wù)器
- Cookie 在做跨頁面通信時(shí),由于帶上所有內(nèi)容,導(dǎo)致上傳數(shù)據(jù) + 請求變慢,Storage 的出現(xiàn)解決了 Cookie 的痛點(diǎn),只要將一些不敏感信息存儲(chǔ)在 Storage 中即可
- JS 調(diào)用 Cookie 比較麻煩,一般都用庫進(jìn)行封裝。Storage 調(diào)用起來比較簡單,也可以再次封裝達(dá)到更好的效果
- Cookie 大小 4K 左右,Storage 大小 5M 左右
- 后臺代碼可以任意設(shè)置 Cookie 的過期時(shí)間。Storage 中的 LocalStorage 永久有效,除非用戶刪除,Storage 中的 SessionStorage 在用戶關(guān)閉頁面(Session 結(jié)束)后就失效
LocalStorage + SessionStorage
- 兩者與 HTTP 無關(guān)
- 每個(gè)域名的 LocalStorage | sessionStorage 有最大存儲(chǔ)量,因?yàn)g覽器而異
- 只有相同域名的頁面才能互相讀取 LocalStorage。SessionStorage 只在同一瀏覽器窗口中共享
- LocalStorage 本地存儲(chǔ), SessionStorage 會(huì)話存儲(chǔ)
- LocalStorage 永久有效,除非用戶刪除。SessionStorage 在用戶關(guān)閉頁面(Session 結(jié)束)后就失效
Cache-Control + ETag
- 兩者都是 HTTP 響應(yīng)頭,都可以實(shí)現(xiàn)加快請求 | 響應(yīng)速度
- Cache-Control 是直接使用本地緩存,不會(huì)發(fā)送請求
- ETag 發(fā)送請求,如果 MD5 值相同,則沒有響應(yīng)體