本文介紹本地?cái)?shù)據(jù)存儲(chǔ)的選型。簡(jiǎn)單總結(jié)一些查詢到的關(guān)于本地?cái)?shù)據(jù)存儲(chǔ)的技術(shù)。
控制臺(tái)展示前端存儲(chǔ)
Chrome:

前端離線存儲(chǔ)目前支持 Cookie、localStorage、sessionStorage、IndexedDB、Web SQL Database以及FileSystem.
本地存儲(chǔ)概覽
1)本地?cái)?shù)據(jù)存儲(chǔ):一般存儲(chǔ)的都是數(shù)據(jù)
- Cookie:瀏覽器均支持,容量為4KB,用起來超級(jí)麻煩,而且限制極大;不適合
- IE用戶數(shù)據(jù)(UserData):僅IE支持
- Web存儲(chǔ)機(jī)制(Web Storage):H5,容量為5M??梢宰鰇ey value 的存儲(chǔ)
- localStorage
- sessionStorage :其實(shí)算不上離線存儲(chǔ),因?yàn)榫W(wǎng)頁關(guān)閉了,存儲(chǔ)就會(huì)丟棄。
- globalStorage(Firefox獨(dú)有,F(xiàn)irefox13起就不再支持) - 數(shù)據(jù)庫存儲(chǔ):
- IndexDB:前端的nosql數(shù)據(jù)庫。
- Web SQL:關(guān)系數(shù)據(jù)庫,需要注意的是,HTML5會(huì)放棄Web SQL
2)離線存儲(chǔ)(應(yīng)用緩存):一般存儲(chǔ)的是網(wǎng)頁
- Cache Storage:定義在 Service Worker 的規(guī)范中,配合 Service Worker 進(jìn)行離線緩存
- Application Cache:在定稿的 HTML 5.1 中被拿到了
存儲(chǔ)方式的詳解和比較
1. 小容量的 cookie
1.1 說明
HTTP Cookie,最初用于在客戶端存儲(chǔ)會(huì)話信息。該標(biāo)準(zhǔn)要求服務(wù)器對(duì)任意 HTTP 請(qǐng)求發(fā)送 Set-Cookie HTTP 頭作為響應(yīng)的一部分,其中包含會(huì)話信息。瀏覽器會(huì)存儲(chǔ)這樣的會(huì)話信息,并在這之后,通過為每個(gè)請(qǐng)求添加 Cookie HTTP 頭將信息發(fā)送回服務(wù)器。
【舉例如下】:

1.2 使用
在 JavaScript 中可以通過 document.cookie 設(shè)置字段和進(jìn)行訪問。
// 設(shè)置 cookie 字段
document.cookie = 'name=Lucy';
// 更好的設(shè)置方式:
document.cookie = encodeURIComponent('age') + '=' + encodeURIComponent(25);
// 訪問 cookie
document.cookie
// 刪除 cookie(設(shè)置存儲(chǔ)有效時(shí)長(zhǎng)為過去時(shí)間)
var date = new Date();
date.setDate(date.getDate() + '設(shè)置時(shí)長(zhǎng)');
document.cookie = 'name=Lucy;expires=' + date.toUTCString();

1.3 訪問限制及存儲(chǔ)時(shí)長(zhǎng)
cookie 可以設(shè)置訪問域,在設(shè)置 cookie 的時(shí)候,設(shè)定了 cookie 的訪問域名為一個(gè)頂級(jí)域名,則可以達(dá)到幾個(gè)子域名共享 cookie 的效果,如騰訊網(wǎng) www.qq.com 與微信網(wǎng)頁版 wx.qq.com 共享了 pac_uid,以及不同頁面的購物車共享。
如果設(shè)定了 cookie 的超時(shí)時(shí)間,則 cookie 將在到期的時(shí)候失效。
如果沒有設(shè)定超時(shí)時(shí)間,則是 session 級(jí)別的(注意上圖 Expires/Max-Age 一欄),在退出瀏覽器時(shí),該 cookie 將消失。
cookie 的 session 不同于 sessionStorage 的 session,cookie 的 session 是指在未關(guān)閉瀏覽器的情況下,所有的 tab 級(jí)別的頁面或新開,或刷新,均屬于一個(gè) session。
1.4 cookie 的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
可控制過期時(shí)間,使其不會(huì)長(zhǎng)期有效
可擴(kuò)展、可用性比較好,可跨域共享
可加密減少cookie被破解的可能性
兼容性好
缺點(diǎn):
數(shù)量和長(zhǎng)度有限制
在請(qǐng)求頭上帶著數(shù)據(jù)安全性差

1.5 應(yīng)用場(chǎng)景
一般非到不得已,不要在 cookie 里面存東西,更不要存儲(chǔ)重要和敏感的數(shù)據(jù)。如果要存儲(chǔ)的話,建議存儲(chǔ)一些同步訪問頁面的時(shí)候必須要被帶到服務(wù)端的信息。
客戶端登錄,用于保存用戶信息。如“下次自動(dòng)登錄”的選項(xiàng),勾選之后下次就不需要重復(fù)驗(yàn)證。通過 cookie 可以保存用戶的 id。
創(chuàng)建購物車??梢詫?shí)現(xiàn)不同頁面之間的數(shù)據(jù)同步(同一個(gè)域名下是可以共享cookie的),同時(shí)在提交訂單的時(shí)候又會(huì)把這些cookie傳到后臺(tái)。
跟蹤用戶行為。例如百度聯(lián)盟會(huì)通過cookie記錄用戶的偏好信息,從而推薦個(gè)性化推廣信息,如頁面上的小廣告。這是可以禁用的,也是cookie的缺點(diǎn)之一。
2. Web Storage:僅存儲(chǔ)于客戶端
定義了兩種用于存儲(chǔ)數(shù)據(jù)的對(duì)象:sessionStorage 和 localStorage,后兩者是Storage的實(shí)例。
注意:Storage 類型只能存儲(chǔ)字符串。非字符串的數(shù)據(jù)在存儲(chǔ)之前會(huì)被轉(zhuǎn)換成字符串。

2.1 目的及兩個(gè)主要目標(biāo)
- 克服由 cookie 帶來的一些限制,當(dāng)數(shù)據(jù)需要被嚴(yán)格控制在客戶端上時(shí),無須持續(xù)地將數(shù)據(jù)發(fā)回服務(wù)器。
- (目標(biāo))提供一種在 cookie 之外存儲(chǔ)會(huì)話數(shù)據(jù)的途徑;
- (目標(biāo))提供一種存儲(chǔ)大量可以跨會(huì)話存在的數(shù)據(jù)的機(jī)制。
2.2 訪問方式
這兩個(gè)對(duì)象在瀏覽器中都是以 windows 對(duì)象屬性的形式存在,在 JavaScript 中可以直接通過 sessionStorage 和 localStorage 訪問。
2.3 Storage 的實(shí)例方法
- clear:刪除所有值,F(xiàn)irefox中沒有實(shí)現(xiàn)
- getItem(name):根據(jù)指定的名字 name 獲取對(duì)應(yīng)的值
- key(index):獲得 index 處的值的名字
- removeItem(name):刪除由 name 指定的名值對(duì)兒
- setItem(name, value):為指定的 name 設(shè)置一個(gè)對(duì)應(yīng)的值
2.4 sessionStorage 和 localStorage 介紹
短暫的 sessionStorage
1) 使用:

2) 訪問限制:
同源策略:不同于cookie,sessionStorage訪問限制更高,只有當(dāng)前設(shè)定sessionStorage的域下才能訪問;
單標(biāo)簽頁:兩個(gè)tab(相同域)之間不能互通;
在新開的tab下或者關(guān)閉本tab之后再打開,也不能訪問之前寫下的sessionStorage;
刷新本tab可以訪問。
3) 特點(diǎn)及應(yīng)用場(chǎng)景:
主要用于僅針對(duì)會(huì)話的小段數(shù)據(jù)的存儲(chǔ)。
建議存儲(chǔ)一些當(dāng)前頁面刷新需要存儲(chǔ),且不需要在tab關(guān)閉時(shí)候留下的信息。
可以用來檢測(cè)用戶是否是刷新進(jìn)入的頁面,如音樂播放器恢復(fù)播放進(jìn)度條的功能。
非常適合單頁應(yīng)用程序,可以方便在各業(yè)務(wù)模塊進(jìn)行傳值。
持久的 localStorage(隱身窗口例外)
使用: 同 sessionStorage
訪問限制:
同源策略:同 sessionStorage,要訪問同一個(gè) localStorage,頁面必須來自同一個(gè)域名(子域名無效),使用同一種協(xié)議,在同一個(gè)端口上;
localStorage 設(shè)定后,刷新或新開 tab 是可以訪問到的,關(guān)閉瀏覽器重新打開原先tab也可訪問。
- 特點(diǎn)及應(yīng)用場(chǎng)景:
持久保存客戶端數(shù)據(jù),數(shù)據(jù)保留到通過JavaScript刪除或者用戶清除瀏覽器緩存。
如果有一些數(shù)據(jù),服務(wù)器難以承載其壓力,但又要與用戶的信息綁定,可以使用 localStorage 存儲(chǔ)一些狀態(tài),這樣既能緩解服務(wù)器壓力,也可以存儲(chǔ)用戶的數(shù)據(jù)。
數(shù)據(jù)比較大的臨時(shí)保存方案。如在線編輯文章時(shí)的自動(dòng)保存。
多頁面訪問共同數(shù)據(jù)。sessionStorage只適用于同一個(gè)標(biāo)簽頁,localStorage相比而言可以在多個(gè)標(biāo)簽頁中共享數(shù)據(jù)。
localStorage 與 sessionStorage 的區(qū)別總結(jié)
H5的兩種存儲(chǔ)技術(shù)的最大區(qū)別就是生命周期
localStorage是本地存儲(chǔ),存儲(chǔ)期限不限;
sessionStorage是會(huì)話存儲(chǔ),頁面關(guān)閉數(shù)據(jù)就會(huì)丟失。
sessionStorage 有單標(biāo)簽頁限制,localStorage 則沒有。
2.5 Storage 存儲(chǔ)類型說明
Storage 類型只能存儲(chǔ)字符串。如果存儲(chǔ)的是對(duì)象,可以將對(duì)象序列化為字符串再存入。(以下以 localStorage 進(jìn)行說明,sessionStorage 同樣適用)
2.6 Storage 大小限制及檢測(cè)
Web Storage 的限制因?yàn)g覽器而異。一般來說,對(duì)存儲(chǔ)空間大小的限制都是以每個(gè)來源(協(xié)議、域和端口)為單位的,即每個(gè)來源都有固定大小的空間用于保存自己的數(shù)據(jù)。對(duì)于 localStorage 而言,大多數(shù)桌面瀏覽器會(huì)設(shè)置每個(gè)來源 5MB 的限制。對(duì) sessionStorage 的限制也是因?yàn)g覽器而異。
對(duì)于 Storage 的大小檢測(cè),可以將 localStorage 和 sessionStorage 序列化,然后查看其字節(jié)數(shù):
2.7 storage 事件
對(duì) Storage 對(duì)象進(jìn)行任何操作,都會(huì)在文檔上觸發(fā) storage 事件,該事件的 event 對(duì)象有以下屬性:
domain:發(fā)生變化的存儲(chǔ)空間的域名
key:設(shè)置或者刪除的鍵名
newValue:如果是設(shè)置值,則是新值;如果是刪除鍵,則是null
oldValue:鍵被更改之前的值
使用時(shí)需要檢測(cè) WebKit 是否支持 storage 事件。
2.8 Web Storage 和 Cookie 的區(qū)別總結(jié)
Web Storage 是為了更大容量存儲(chǔ)設(shè)計(jì)的,而Cookie的大小是受限的。
cookie 在每次請(qǐng)求一個(gè)新的頁面的時(shí)候都會(huì)被發(fā)送過去,在瀏覽器和服務(wù)器間來回傳遞,這樣無形中浪費(fèi)了帶寬。
cookie 可以設(shè)定訪問域,在同源窗口中可以共享,而 web storage 受同源策略限制。
但是Cookie也是不可以或缺的:Cookie的作用是與服務(wù)器進(jìn)行交互,作為HTTP規(guī)范的一部分而存在 ,而Web Storage僅僅是為了在本地“存儲(chǔ)”數(shù)據(jù)而生。
3. 數(shù)據(jù)庫:IndexDB 與 Web SQL
3.1 兩者的特點(diǎn)
websql 像關(guān)系型數(shù)據(jù)庫,使用 sql 語句進(jìn)行操作。
indexdb 像 nosql,直接使用 js 方法操作數(shù)據(jù)。
訪問:indexdb 和 websql 與 web storage 一致,均是在創(chuàng)建數(shù)據(jù)庫的域名下才能訪問。且不能指定訪問域名。
存儲(chǔ)時(shí)間:這兩位的存儲(chǔ)時(shí)間也是永久,除非用戶清除數(shù)據(jù),可以用作長(zhǎng)效的存儲(chǔ)。
大小限制:理論上講,這兩種存儲(chǔ)的方式是沒有大小限制的。然而indexeddb的數(shù)據(jù)庫超過50M的時(shí)候?yàn)g覽器會(huì)彈出確認(rèn)?;旧弦蚕喈?dāng)于沒有限制了。
性能測(cè)試:indexeddb查詢少量數(shù)據(jù)花費(fèi)差不多20MS左右。大量數(shù)據(jù)的情況下,相對(duì)耗時(shí)會(huì)變長(zhǎng)一些,但是也就在30MS左右,也是相當(dāng)給力了,10W數(shù)據(jù)+,畢竟nosql。
而 websql 的效率也不錯(cuò),10w+數(shù)據(jù),簡(jiǎn)單查詢一下,只花費(fèi)了20MS左右。
3.2 IndexDB 特點(diǎn)
它的數(shù)據(jù)不是保存在表中,而是保存在對(duì)象存儲(chǔ)空間中。
創(chuàng)建對(duì)象存儲(chǔ)空間時(shí),需要定義一個(gè)鍵,然后就可以添加數(shù)據(jù)。
可以使用游標(biāo)在對(duì)象存儲(chǔ)空間中查詢特定的對(duì)象。
而索引則是為了提高查詢速度而基于特定的屬性創(chuàng)建的。
說明:indexDB 目前兼容性還不是很好,Web SQL 雖然已經(jīng)過時(shí),但是其兼容性卻非常好,幾乎是移動(dòng)端均可用(兼容性對(duì)比查看請(qǐng)移步:http://caniuse.com/#search=websql 以及 http://caniuse.com/#search=indexdb)。
因此,能用 indexdb,就用 indexdb,因?yàn)槠浯砹宋磥淼陌l(fā)展方向,如果不能使用盡量使用 websql 進(jìn)行代替。
3.3 IndexDB 異步 API
IndexDB 設(shè)計(jì)的操作完全是異步的。因此,大多數(shù)操作會(huì)以請(qǐng)求的方式進(jìn)行,但這些操作會(huì)在后期執(zhí)行,如果成功則返回結(jié)果,如果失敗則返回錯(cuò)誤。差不多每一次 IndexDB 操作,都需要你注冊(cè) onerror 或 onsuccess 事件處理程序,以確保適當(dāng)?shù)靥幚斫Y(jié)果。

打開數(shù)據(jù)庫時(shí),實(shí)質(zhì)上返回了一個(gè)DB對(duì)象,該對(duì)象存在于 result 中。
3.4 Web SQL Database
需要注意的是,HTML5已經(jīng)會(huì)放棄Web SQL Database
放棄的原因如下:
This document was on the W3C Recommendation track but specification work has stopped. The specification reached an impasse: all interested implementors have used the same SQL backend (Sqlite), but we need multiple independent implementations to proceed along a standardisation path.
大概意思是:
該文件是W3C推薦標(biāo)準(zhǔn),但規(guī)范的制定工作已經(jīng)停止。該規(guī)范陷入僵局:所有感興趣的實(shí)現(xiàn)者都使用了相同的SQL后端(SQLite的),但我們需要多個(gè)獨(dú)立的實(shí)現(xiàn)沿著規(guī)范化的路徑進(jìn)行。
翻譯參考:http://www.zhihu.com/question/41951041
有關(guān)標(biāo)準(zhǔn)參考: https://www.w3.org/TR/webdatabase/
如果你掌握過mysql,orancle,mssql等關(guān)系型數(shù)據(jù)庫,對(duì)于websql的理解還是比較容易的。
websql的關(guān)鍵技術(shù)點(diǎn)分為:
測(cè)試瀏覽器支持
創(chuàng)建數(shù)據(jù)庫
創(chuàng)建表格
插入數(shù)據(jù)等
用到的幾個(gè)核心方法:
openDatabase openDatabaseSycn 創(chuàng)建數(shù)據(jù)庫
transaction起事務(wù)
executeSql執(zhí)行sql語句
詳情可以參考:http://blog.csdn.net/netcy/article/details/52188531
3.5 應(yīng)用場(chǎng)景:
當(dāng)我們是在做一個(gè)離線應(yīng)用,或者webapp的時(shí)候,可以考慮使用本地?cái)?shù)據(jù)庫中存取數(shù)據(jù)。如果不存大量的數(shù)據(jù)的話,其實(shí)localStorage就夠用了。亦或者,你想把一張用戶的皮膚圖片之類的大量數(shù)據(jù)存入客戶端緩存起來,localStorage已經(jīng)不夠用了的話,也可以嘗試一下websql與indexeddb。
4.WebSQL
Web SQL是關(guān)系數(shù)據(jù)庫,通過SQL語句訪問
Web SQL 數(shù)據(jù)庫 API 并不是 HTML5 規(guī)范的一部分,但是它是一個(gè)獨(dú)立的規(guī)范,引入了一組使用 SQL 操作客戶端數(shù)據(jù)庫的 APIs。
支持情況:
Web SQL 數(shù)據(jù)庫可以在最新版的 Safari, Chrome 和 Opera 瀏覽器中工作。
核心方法:
①openDatabase:這個(gè)方法使用現(xiàn)有的數(shù)據(jù)庫或者新建的數(shù)據(jù)庫創(chuàng)建一個(gè)數(shù)據(jù)庫對(duì)象。
②transaction:這個(gè)方法讓我們能夠控制一個(gè)事務(wù),以及基于這種情況執(zhí)行提交或者回滾。
③executeSql:這個(gè)方法用于執(zhí)行實(shí)際的 SQL 查詢。
打開數(shù)據(jù)庫:
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024,fn);
//openDatabase() 方法對(duì)應(yīng)的五個(gè)參數(shù)分別為:數(shù)據(jù)庫名稱、版本號(hào)、描述文本、數(shù)據(jù)庫大小、創(chuàng)建回調(diào)
執(zhí)行查詢操作:
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)');
});
插入數(shù)據(jù):
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)');
tx.executeSql('INSERT INTO WIN (id, name) VALUES (1, "winty")');
tx.executeSql('INSERT INTO WIN (id, name) VALUES (2, "LuckyWinty")');
});
讀取數(shù)據(jù):
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM WIN', [], function (tx, results) {
var len = results.rows.length, i;
msg = "<p>查詢記錄條數(shù): " + len + "</p>";
document.querySelector('#status').innerHTML += msg;
for (i = 0; i < len; i++){
alert(results.rows.item(i).name );
}
}, null);
});
由這些操作可以看出,基本上都是用SQL語句進(jìn)行數(shù)據(jù)庫的相關(guān)操作,如果你會(huì)MySQL的話,這個(gè)應(yīng)該比較容易用。
5.離線緩存(application cache)
本地緩存應(yīng)用所需的文件
使用方法:
①配置manifest文件
頁面上:
<!DOCTYPE HTML>
<html manifest="demo.appcache">
...
</html>
Manifest 文件:
manifest 文件是簡(jiǎn)單的文本文件,它告知瀏覽器被緩存的內(nèi)容(以及不緩存的內(nèi)容)。
manifest 文件可分為三個(gè)部分:
①CACHE MANIFEST - 在此標(biāo)題下列出的文件將在首次下載后進(jìn)行緩存
②NETWORK - 在此標(biāo)題下列出的文件需要與服務(wù)器的連接,且不會(huì)被緩存
③FALLBACK - 在此標(biāo)題下列出的文件規(guī)定當(dāng)頁面無法訪問時(shí)的回退頁面(比如 404 頁面)
完整demo:
CACHE MANIFEST
# 2016-07-24 v1.0.0
/theme.css
/main.js
NETWORK:
login.jsp
FALLBACK:
/html/ /offline.html
服務(wù)器上:manifest文件需要配置正確的MIME-type,即 "text/cache-manifest"。
如Tomcat:
<mime-mapping>
<extension>manifest</extension>
<mime-type>text/cache-manifest</mime-type>
</mime-mapping>
常用API:
核心是applicationCache對(duì)象,有個(gè)status屬性,表示應(yīng)用緩存的當(dāng)前狀態(tài):
0(UNCACHED) : 無緩存, 即沒有與頁面相關(guān)的應(yīng)用緩存
1(IDLE) : 閑置,即應(yīng)用緩存未得到更新
2 (CHECKING) : 檢查中,即正在下載描述文件并檢查更新
3 (DOWNLOADING) : 下載中,即應(yīng)用緩存正在下載描述文件中指定的資源
4 (UPDATEREADY) : 更新完成,所有資源都已下載完畢
5 (IDLE) : 廢棄,即應(yīng)用緩存的描述文件已經(jīng)不存在了,因此頁面無法再訪問應(yīng)用緩存
相關(guān)的事件:
表示應(yīng)用緩存狀態(tài)的改變:
checking : 在瀏覽器為應(yīng)用緩存查找更新時(shí)觸發(fā)
error : 在檢查更新或下載資源期間發(fā)送錯(cuò)誤時(shí)觸發(fā)
noupdate : 在檢查描述文件發(fā)現(xiàn)文件無變化時(shí)觸發(fā)
downloading : 在開始下載應(yīng)用緩存資源時(shí)觸發(fā)
progress:在文件下載應(yīng)用緩存的過程中持續(xù)不斷地下載地觸發(fā)
updateready : 在頁面新的應(yīng)用緩存下載完畢觸發(fā)
cached : 在應(yīng)用緩存完整可用時(shí)觸發(fā)
Application Cache的三個(gè)優(yōu)勢(shì):
① 離線瀏覽
② 提升頁面載入速度
③ 降低服務(wù)器壓力
注意事項(xiàng):
- 瀏覽器對(duì)緩存數(shù)據(jù)的容量限制可能不太一樣(某些瀏覽器設(shè)置的限制是每個(gè)站點(diǎn) 5MB)
- 如果manifest文件,或者內(nèi)部列舉的某一個(gè)文件不能正常下載,整個(gè)更新過程將視為失敗,瀏覽器繼續(xù)全部使用老的緩存
- 引用manifest的html必須與manifest文件同源,在同一個(gè)域下
- 瀏覽器會(huì)自動(dòng)緩存引用manifest文件的HTML文件,這就導(dǎo)致如果改了HTML內(nèi)容,也需要更新版本才能做到更新。
- manifest文件中CACHE則與NETWORK,F(xiàn)ALLBACK的位置順序沒有關(guān)系,如果是隱式聲明需要在最前面
- FALLBACK中的資源必須和manifest文件同源
- 更新完版本后,必須刷新一次才會(huì)啟動(dòng)新版本(會(huì)出現(xiàn)重刷一次頁面的情況),需要添加監(jiān)聽版本事件。
- 站點(diǎn)中的其他頁面即使沒有設(shè)置manifest屬性,請(qǐng)求的資源如果在緩存中也從緩存中訪問
- 當(dāng)manifest文件發(fā)生改變時(shí),資源請(qǐng)求本身也會(huì)觸發(fā)更新
離線緩存與傳統(tǒng)瀏覽器緩存區(qū)別:
離線緩存是針對(duì)整個(gè)應(yīng)用,瀏覽器緩存是單個(gè)文件
離線緩存斷網(wǎng)了還是可以打開頁面,瀏覽器緩存不行
離線緩存可以主動(dòng)通知瀏覽器更新資源
參考: http://www.cnblogs.com/Ruth92/p/6384057.html?utm_source=itdadao&utm_medium=referral
web sql參考:http://blog.csdn.net/netcy/article/details/52188531
IndexedDB參考:http://www.cnblogs.com/dolphinX/p/3415761.html
IndexedDB API:https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API