前言
在項目開發過程中,前端需要存儲大量的數據。cookie, localstorage 都有存儲長度限制。
表格一覽
特性 | cookie | localStorage | sessionStorage | indexedDB |
---|---|---|---|---|
數據生命周期 | 一般由服務器生成,可以設置過期時間;前端采用和js-cookie等組件也可以生成 | 除非被清理,否則一直存在;瀏覽器關閉還會保存在本地,但是不支持跨瀏覽器 | 頁面關閉就清理刷新依然存在,不支持跨頁面交互 | 除非被清理,否則一直存在 |
數據存儲大小 | 4K | 5M | 5M | 不限制大小 |
與服務端通信 | 每次都會攜帶在請求的header 中,對于請求性能有影響;同時由于請求中都帶有,所以也容易出現安全問題 | 不參與 | 不參與 | 不參與 |
特點 | 字符串鍵值對在本地存儲數據 | 字符串鍵值對在本地存儲數據 | 字符串鍵值對在本地存儲數據 | IndexedDB 是一個非關系型數據庫(不支持通過 SQL 語句操作)??梢源鎯Υ罅繑祿?,提供接口來查詢,還可以建立索引,這些都是其他存儲方案無法提供的能力。 |
需要一個存儲容量大,支持搜索和自定義索引的前端存儲方案,就選用了 。
caniuse上查看 indexedDB 支持情況,目前瀏覽器支持情況良好。
caniuse.com/?search=ind…
IndexedDB介紹
IndexedDB 屬于非關系型數據庫。(不支持SQL查詢)
特點:
- 鍵值對儲存 IndexedDB 內部采用對象倉庫(object store)存放數據。所有類型的數據都可以直接存入,包括 JAVAScript 對象。對象倉庫中,數據以"鍵值對"的形式保存,每一個數據記錄都有對應的主鍵,主鍵是獨一無二的,不能有重復,否則會拋出一個錯誤。
- 異步 IndexedDB 操作時不會鎖死瀏覽器,用戶依然可以進行其他操作,這與 LocalStorage 形成對比,后者的操作是同步的。異步設計是為了防止大量數據的讀寫,拖慢網頁的表現。
- 支持事務 IndexedDB 支持事務(transaction),這意味著一系列操作步驟之中,只要有一步失敗,整個事務就都取消,數據庫回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況。
- 同源限制 IndexedDB 受到同源限制,每一個數據庫對應創建它的域名。網頁只能訪問自身域名下的數據庫,而不能訪問跨域的數據庫。
- 支持二進制儲存 IndexedDB 不僅可以儲存字符串,還可以儲存二進制數據(ArrayBuffer 對象和 Blob 對象。
- 儲存空間大 IndexedDB 的儲存空間比 LocalStorage 大得多,一般來說不少于 250MB,甚至沒有上限。儲 存 在 電 腦 上 中 的 位 置 為 C:Users當 前 的 登 錄 用 戶AppDataLocalgoogleChromeUser DataDefaultIndexedDB
核心概念
- 數據庫:IDBDatabase 對象,數據庫有版本概念,同一時刻只能有一個版本,每個域名可以建多個數據庫
- 對象倉庫:IDBObjectStore 對象,類似于關系型數據庫的表格
- 索引: IDBIndex 對象,可以在對象倉庫中,為不同的屬性建立索引,主鍵建立默認索引
- 事務: IDBTransaction 對象,增刪改查都需要通過事務來完成,事務對象提供了error,abord,complete三個回調方法,監聽操作結果
- 操作請求:IDBRequest 對象
- 指針: IDBCursor 對象
- 主鍵集合:IDBKeyRange 對象,主鍵是默認建立索引的屬性,可以取當前層級的某個屬性,也可以指定下一層對象的屬性,還可以是一個遞增的整數
indexedDB使用
基礎操作
1. 創建數據庫 & 新建表和索引
typescript
復制代碼
/* *@databaseName 數據倉庫的名字 *@version 數據倉庫的版本 *@databaseName 數據倉庫的名字 */ var request = window.indexedDB.open('group', 1); /* *數據倉庫打開失敗 */ request.onerror = function(error) { console.log('IndexedDB 打開失敗', error); }; /* *數據倉庫打開成功 */ request.onsuccess = function(res) { console.log('IndexedDB 打開成功', res); db = res.target.result; }; /* *數據倉庫升級事件(第一次新建庫是也會觸發,因為數據倉庫從無到有算是升級了一次) */ request.onupgradeneeded = function(res) { console.log('IndexedDB 升級成功', res); db = res.target.result; db_table = db.createObjectStore('group', { keyPath: 'id' }); db_table.createIndex('indexName', 'name', { unique: false }); };
2. 新增數據
typescript
復制代碼
/* *新建事務 *@params 數據倉庫的數組 *@params 寫入模式 */ var store = db.transaction(['group'], 'readwrite').objectStore('group'); /* *add方法添加數據 *@params 需要添加的數據信息 */ var request = store.add({ id: new Date().getTime(), name: '王二', age: 12, emAIl: 'XXXX@xxx.com', }); /* *添加成功 */ request.onsuccess = function(event) { console.log('數據添加成功', event); }; /* *添加失敗 */ request.onerror = function(event) { console.log('數據添加失敗', event); };
3. 讀取數據
typescript
復制代碼
/* *新建事務 *@params 數據倉庫的數組 */ var store = db.transaction(['group']).objectStore('group'); /* *get方法獲取數據 *@params 數據的主鍵 */ var request = store.get(1678664831491); /* *獲取成功 */ request.onsuccess = function(event) { if (event.target.result) { console.log('數據獲取成功', event.target.result); } else { console.log('未獲取到數據'); } }; /* *獲取失敗 */ request.onerror = function(event) { console.log('數據獲取失敗', event); };
4. 更新數據
typescript
復制代碼
/* *新建事務 *@params 數據倉庫的數組 *@params 寫入模式 */ var store = db.transaction(['group'], 'readwrite').objectStore('group'); /* *put方法根據主鍵更新數據 *@params 數據的主鍵 */ var request = store.put({ id: 1678664831491, name: '張一' + Math.random(), age: 24, email: 'zhangsan@example.com', }); /* *更新成功 */ request.onsuccess = function(event) { console.log('數據更新成功', event); }; /* *更新失敗 */ request.onerror = function(event) { console.log('數據更新失敗', event); };
未加 readwrite, 會拋錯,修改數據失敗
5. 刪除數據
typescript
復制代碼
/* *新建事務 *@params 數據倉庫的數組 */ var store = db.transaction(['group'], 'readwrite').objectStore('group'); /* *delete方法刪除數據 *@params 數據的主鍵 */ var request = store.delete(1678664831491); /* *刪除成功 */ request.onsuccess = function (event) { console.log('數據刪除成功',event); }; /* *刪除失敗 */ request.onerror = function (event) { console.log('數據刪除失敗',event); };
6. 使用索引
typescript
復制代碼
/* *新建事務 *@params 數據倉庫的數組 */ var store = db.transaction(['group']).objectStore('group'); /* *index方法獲取索引對象 *get方法獲取數據 *@params 數據的索引 */ var request = store.index('indexName').get('張四'); /* *獲取成功 */ request.onsuccess = function (event) { console.log('通過索引獲取數據成功',event.target.result); }; /* *獲取失敗 */ request.onerror = function (event) { console.log('通過索引獲取數據失敗',event); };
7. 獲取整張表所有的data
typescript
復制代碼
var store = db.transaction(['group']).objectStore('group'); var request = store.getAll(); /* *更新成功 */ request.onsuccess = function(event) { console.log('indexedDB getAll:', event.target.result); }; /* *更新失敗 */ request.onerror = function(event) { console.log('indexedDB getAll:', event); };
8. 根據指定條件獲取data
首先讓我們 來了解 IDBKeyRange
的API
www.w3cschool.cn/javascript_…
typescript
復制代碼
var store = db.transaction(['group']).objectStore('group'); // 獲取id名稱小于當前時間的所有data var request = store.getAll(IDBKeyRange.upperBound(+new Date())); /* *更新成功 */ request.onsuccess = function(event) { console.log('indexedDB getAll:', event.target.result); }; /* *更新失敗 */ request.onerror = function(event) { console.log('indexedDB getAll:', event); };
業務中優雅使用
indexedDB 并非無底洞,可以無限存儲。要考慮做定期刪除等功能
1. 定期刪除失效數據
1. 首先我們創建數據的時候就以時間戳+失效時間來約定id規則
2. 再通過上面基礎操作的getAll
方法,獲取指定條件的data
,再遍歷data
,調用刪除數據API
JavaScript
復制代碼
var store = db.transaction(['group'], 'readwrite').objectStore('group'); var request = store.getAll(IDBKeyRange.upperBound(+new Date())); /* *更新成功 */ request.onsuccess = function(event) { console.log('indexedDB getAll:', event); console.log('indexedDB getAll:', event.target.result); const data = event.target.result; data.forEach(item => { console.log('刪除數據', item); const deletRequest = store.delete(item.id); /* *刪除成功 */ deletRequest.onsuccess = function(event) { console.log('數據刪除成功', event); }; /* *刪除失敗 */ deletRequest.onerror = function(event) { console.log('數據刪除失敗', event); }; }); }; /* *更新失敗 */ request.onerror = function(event) {};
3. 我們把上述方法包裝下每次打開頁面,清空下失效數據。就可以實現一個定期刪除失效數據的方法啦
2. 批量添加數據
typescript
復制代碼
const TestData = [ { event: 'NE-TEST1', level: 'warning', errorCode: 200, url: 'http://www.example.com', time: '2017/11/8 下午4:53:039', isUploaded: false }, { event: 'NE-TEST2', msg: '測試2', level: 'error', errorCode: 1000, url: 'http://www.example.com', time: '2017/11/8 下午4:53:042', isUploaded: false }, { event: 'NE-TEST3', msg: '測試3', level: 'info', errorCode: 3000, url: 'http://www.example.com', time: '2017/11/8 下午4:53:043', isUploaded: false }, { event: 'NE-TEST4', mgs: '測試4', level: 'info', url: 'http://www.example.com', time: '2017/11/8 下午4:53:0423', isUploaded: false } ] /** * 添加數據 * @param {array} docs 要添加數據 * @param {string} objName 倉庫名稱 */ function addData (docs, objName) { if (!(docs && docs.length)) { throw new Error('docs must be a array!') } return openIndexedDB().then(db => { const tx = db.transaction([objName], 'readwrite') tx.oncomplete = e => { console.log('tx:addData onsuccess', e) return Promise.resolve(docs) } tx.onerror = e => { e.stopPropagation() console.error('tx:addData onerror', e.target.error) return Promise.reject(e.target.error) } tx.onabort = e => { console.warn('tx:addData abort', e.target) return Promise.reject(e.target.error) } const obj = tx.objectStore(objName) docs.forEach(doc => { const req = obj.add(doc) /** * NOTE: * request * 兩個事件: * 1. success * 2. error */ // req.onsuccess = e => console.log('obj:addData onsuccess', e.target) req.onerror = e => { console.error('obj:addData onerror', e.target.error) } }) }) } addData(TestData, OB_NAMES.UseKeyGenerator) .then(() => addData(TestData, OB_NAMES.UseKeyPath))
作者:叫叫技術團隊
鏈接:https://juejin.cn/post/7239259798267904059
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。