前端数据库indexedDB入门
什么是indexDB
????????學習文檔:
- 阮一峰老師的分享文檔:瀏覽器數據庫 IndexedDB 入門教程 - 阮一峰的網絡日志
- 官方API文檔,純英文,不過提供的代碼很有用:Indexed Database API 3.0
????????indexDB是HTML5的新概念,indexedDB是一個用于在瀏覽器中存儲較大數據結構的Web API,并且提供了索引功能以實現高性能查找。不同于其他基于SQL的關系型數據庫,indexedDB是一個事務型的數據庫系統,會將數據集作為個體對象存儲,數據形式使用的是JSON,而不是列數固定的表格來存儲數據的。
????????indexDB比本地存儲很強大,而且存儲大小是250m以上(受計算機硬件和瀏覽器廠商的限制)。
- indexDB優點是:存儲容量大;支持異步操作;具有事務特點;
- indexDB缺點是:不支持DO操作;不能跨域。
????????indexDB中的對象:
- 數據庫:IDBDatabase 對象
- 對象倉庫:IDBObjectStore 對象
- 索引: IDBIndex 對象
- 事務: IDBTransaction 對象
- 操作請求:IDBRequest 對象
- 指針(游標): IDBCursor 對象
- 主鍵集合:IDBKeyRange 對象
????????基本語法:
| 語法 | 作用 |
| window.indexedDB.open(數據庫名,版本號) | 打開數據庫(如果數據庫不存在則創建一個新的庫) |
| .onerror | 數據庫操作過程中出錯時觸發 |
| .onupgradeneeded | 創建一個新的數據庫或者修改數據庫版本號時觸發 |
| .onsuccess | 數據庫成功完成所有操作時觸發 |
| .createObjectStore(對象倉庫名稱,keypath) | 創建對象倉庫 |
| .createIndex(索引名稱,keypath,objectParameters) | 建立索引 |
| .transaction(對象倉庫名稱) || .transaction(對象倉庫名稱,‘readwrite’) | 創建一個事務 || 創建一個事務,并要求具有讀寫權限 |
| .objectStore(對象倉庫名稱) | 獲取對象倉庫 |
| .get ( num ) || .getAll() | 獲取數據 || 獲取全部數據 |
| .add( data ) | 添加數據 |
| .put( newdata ) | 修改數據 |
| .delete ( keypath ) | 刪除數據 |
????????Demo:
????????關于具體應用的demo可以看我的gitee上的Demo:
https://gitee.com/yinlingyun123/dragVuehttps://gitee.com/yinlingyun123/dragVue
indexDB基礎操作:
????????indeDB和一般的數據庫一樣,創建/打開數據庫,創建/打開表,對數據的增刪查改等都是其基本功能。
????????1、判斷瀏覽器是否支持IndexDB
function justifyIndexDEB(){if("indexedDB" in window) {// 支持console.log(" 支持indexedDB...");} else {// 不支持console.log("不支持indexedDB...");} }????????2、不同瀏覽器兼容
//數據庫對象 window.indexedDB =window.indexedDB||window.webikitIndexedDB||window.mozIndexedDB||window.msIndexedDB; //數據庫事務 window.IDBTransaction = window.IDBTransaction||window.webikitIDBTransaction||window.mozIDBTransaction||window.msIDBTransaction; //數據庫查詢條件window.IDBKeyRange = window.IDBKeyRange||window.webkitIDBKeyRange||window.mozIDBKeyRange||window.msIDBKeyRange; //游標 window.IBDCursor = window.IBDCursor ||window.webkitIBDCursor ||window.mozIBDCursor ||window.msIBDCursor ;????????3、創建/打開數據庫
????????如果存在就打開,不存在就創建一個indexDB數據庫:
const req = window.indexedDB.open(databaseName, version) // 數據庫名稱,版本號 let that = this; req.onsuccess = function (event) { // 監聽數據庫創建成功事件that.indexDB = event.target.result // 數據庫對象console.log('數據庫打開成功') } req.onerror = function (error) {console.log('數據庫打開報錯') } req.onupgradeneeded = function (event) {// 數據庫創建或升級的時候會觸發console.log('數據庫創建或升級') }????????4、創建/打開/刪除數據表
????????在indexedDB中,是使用objectStore來存儲數據呢。objectStore相當于一張表,但是objectStore并不想mysql中的表一樣,具有一列一列的結構,它只有兩列,一列是keypath(鍵值),另一列就是存儲的數據了,存儲的數據一般用JavaScript中的對象來表示。每一條數據都和一個鍵相關聯。
????????存儲時可以使用每條記錄中的某個指定字段作為鍵值(keyPath),也可以使用自動生成的遞增數字作為鍵值(keyGenerator),也可以不指定。選擇鍵的類型不同,objectStore可以存儲的數據結構也有差異。
| 鍵類型 | 存儲數據 |
| 不使用 | 任意值,但是沒添加一條數據的時候需要指定鍵參數 |
| keyPath | Javascript對象,對象必須有一屬性作為鍵值 |
| keyGenerator | 任意值 |
| 都使用 | Javascript對象,如果對象中有keyPath指定的屬性則不生成新的鍵值,如果沒有自動生成遞增鍵值,填充keyPath指定屬性 |
????????創建表:
????????通過數據庫實例的createObjectStore(storeName,keyType)進行創建objectStore。這個方法有兩個參數,一個是objectStore的名字,一個是創建表的鍵類型。
????????創建表相當于修改了數據庫的模式,所以這個操作應該放到onupgradeneeded中:
req.onupgradeneeded = function (event) {// 數據庫創建或升級的時候會觸發let db = event.target.resultlet storeName = 'product' // 表名if(!db.objectStoreNames.contains(storeName)) {// keyPath是主鍵鍵值,也可以不傳然后設定autoIncrement:true自動創建鍵值db.createObjectStore(storeName,{keyPath:'key'});}//db.deleteObjectStore(storeName);刪除數據倉庫方法,參數為數據倉庫名稱 }????????5、創建/刪除索引
????????索引可以用來搜索,主鍵是默認的索引。
????????只能針對被設為索引的屬性值進行檢索,不能針對沒有被設為索引的屬性值進行檢索。
????????索引包含有聯合索引,唯一索引,對數組字段建索引等等擴展功能,有需要的可以自己探索!
req.onupgradeneeded = function (event) {// 數據庫創建或升級的時候會觸發db = event.target.resultlet storeName = 'product' // 表名if (!db.objectStoreNames.contains(storeName)) { // 判斷表是否存在let objectStore = db.createObjectStore(storeName, { keyPath: 'key',autoIncrement: true })// 創建索引// indexName索引列名稱// indexKey索引鍵值objectStore.createIndex('indexName', 'indexKey', { unique: false }) // 創建索引 可以讓你搜索任意字段//objectStore.deleteIndex("indexName");刪除索引} }????????給對象倉庫(數據庫表)創建索引,需要使用對象倉庫的createIndex()函數,param1 索引名稱,param2 配置索引的鍵值,param3 配置對象 配置該屬性是否是唯一的。
????????param3 配置對象可配置屬性:
- unique 唯一
- multiEntry 對于有多個值的主鍵數組,每個值將在索引里面新建一個條目,否則主鍵數組對應一個條目
????????6、添加數據
// this.indexDB創建數據庫時候存下的數據庫對象 // itemName是存儲的key // newValue是存儲的value const transaction = this.indexDB.transaction('product', "readwrite"); const store = transaction.objectStore('product'); const request = store.put({ key: 'item', value: '<p>3333</p>' }); // const request = store.add({ key: 'item', value: '<p>3333</p>' }); //add方法也可以 request.onsuccess=function(e) {console.info('添加數據成功') }; request.onerror=function(e) {console.info('添加數據失敗') };????????7、獲取數據
????????get方法是獲取定義的主鍵鍵值為輸入數據的第一條數據,如果想獲取匹配的所有數據就得用getAll方法進行獲取。不過get、getAll方法需要查詢整張表來獲得結果,數據量大的時候效率很低。所以在日常使用中都是通過添加索引然后使用游標查詢索引獲取想要的數據。
// this.indexDB創建數據庫時候存下的數據庫對象 // itemName是存儲的key // newValue是存儲的value const transaction = this.indexDB.transaction('product', 'readwrite'); const store = transaction.objectStore('product'); const request=store.get('item'); request.onsuccess=function(e) {store.put({ key: 'item', value: '<p>3333</p>' });console.info('獲取數據成功') }; request.onerror=function(e) {console.info('獲取數據失敗') };????????8、更新數據
????????更新數據操作其實就是添加和獲取合二為一,在獲取成功的回調函數里面進行數據的添加從而覆蓋掉原來的數據:
// this.indexDB創建數據庫時候存下的數據庫對象 // itemName是存儲的key // newValue是存儲的value const transaction = this.indexDB.transaction('product', 'readwrite'); const store = transaction.objectStore('product'); const request=store.get('item'); request.onsuccess=function(e) {store.put({ key: 'item', value: '<p>4444</p>' }); };?????????9、刪除數據
// this.indexDB創建數據庫時候存下的數據庫對象 // itemName是存儲的key // newValue是存儲的value const transaction = this.indexDB.transaction('product', 'readwrite'); const store = transaction.objectStore('product'); const request=store.delete('item'); request.onsuccess=function(e) {console.info('刪除數據成功') }; request.onerror=function(e) {console.info('刪除數據失敗') };????????10、遍歷數據
????????使用指針對象IDBCursor遍歷數據
objectStore.openCursor().onsuccess = function () {const cursor = e.target.resultif (cursor) {console.log(cursor.key) // 當前遍歷數據的主鍵console.log(cursor.value) // 當前遍歷的數據cursor.continue() // 繼續下一個} }????????可以通過配置IDBKeyRange來設定查詢范圍
// 游標查詢范圍內的多個: // 除了bound 還有 only,lowerBound, upperBound 方法,還可以指明是否排除邊界值 const range = IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3]) // 傳入的 prev 表示是降序遍歷游標,默認是next表示升序;如果索引不是unique的,而你又不想訪問重復的索引,可以使用nextunique或prevunique,這時每次會得到key最小的那個數據 dbIndex.openCursor(range, "prev").onsuccess = e => { let cursor = e.target.result;if (cursor) {let data = cursor.value // 數據的處理就在這里。。。 [ 理解 cursor.key,cursor.primaryKey,cursor.value ]cursor.continue()} else {// 游標遍歷結束! } }????????需要說明的是?IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3])? ?的范圍如下:
max1 > min1 || max1 === min1 && max2 > min2 || max1 === min1 && max2 === min2 && max3 > min3 // 好好理解一下這個 bound 的含義吧 !?????????11、使用promise封裝上述方法
? ? ? ? a.使用promise封裝 open方法
/*** 打開/創建數據庫* @param {object} dbName 數據庫的名字* @param {string} storeName 倉庫名稱* @param {string} version 數據庫的版本* @param {string} keyPath 主鍵鍵值,不傳就自動創建主鍵* @param {Array} index 索引數組* @return {object} 該函數會返回一個數據庫實例*/ type StoreOptions = {autoIncrement: boolean;keyPath?: string; }; export const openDB = function (dbName: string,version: number,storeName: string,keyPath?: string,index?: Array<any[]> | undefined ) {return new Promise((resolve, reject) => {// 兼容瀏覽器// eslint-disable-next-line @typescript-eslint/ban-ts-comment// @ts-ignoreconst { webkitIndexedDB, indexedDB, mozIndexedDB, msIndexedDB } = window;const indexDB = indexedDB || mozIndexedDB || webkitIndexedDB || msIndexedDB;let db = null;const request = indexDB.open(dbName, version);// 操作成功request.onsuccess = function (event: any) {db = event?.target?.result; // 數據庫對象resolve({ code: 0, success: true, data: db, msg: "數據庫打開成功!" });};// 操作失敗request.onerror = function () {resolve({ code: -1, success: false, data: null, msg: "數據庫打開失敗!" });};// 創建表和索引request.onupgradeneeded = function (event: any) {// 數據庫創建或升級的時候會觸發db = event?.target?.result; // 數據庫對象const storeOptions: StoreOptions = {autoIncrement: true,};if (keyPath && keyPath !== "") {storeOptions.autoIncrement = false;storeOptions.keyPath = keyPath;}// 創建表if (!db.objectStoreNames.contains(storeName)) {const store = db.createObjectStore(storeName, storeOptions);// 創建索引// indexName索引列名稱// indexKey索引鍵值if (index && index.length > 0) {index.forEach((item: any) => {if (!item.indexName ||!item.indexKey ||item.options.unique === undefined) {reject("索引格式錯誤,請參照格式{indexName:'indexName',indexKey:'indexKey',{unique: false}}");}store.createIndex(item.indexName, item.indexKey, item.options);});}}};}); };????????b.使用promise封裝 add方法?
/*** 新增數據* @param {object} db 數據庫實例* @param {string} storeName 倉庫名稱* @param {object} dataConfig 添加的數據集合**/ export const addData = function (db: any, storeName: string, dataConfig: any) {return new Promise((resolve, reject) => {if (!db) {reject("數據庫不存在或沒有初始化");}if (!dataConfig || !dataConfig.value) {reject("value是必傳項,參照格式{[keyPath]:'key',value:'value'}");}const req = db.transaction([storeName], "readwrite").objectStore(storeName) // 倉庫對象.add(dataConfig);// 操作成功req.onsuccess = function () {resolve({ code: 0, success: true, data: null, msg: "數據寫入成功!" });};// 操作失敗req.onerror = function () {const data = {code: -1,success: false,data: null,msg: "數據寫入失敗!",};resolve(data);};}); };????????c.使用promise封裝 put方法?
/*** 更新數據* @param {object} db 數據庫實例* @param {string} storeName 倉庫名稱* @param {object} dataConfig 更新的數據集合*/ export const updateData = function (db: any,storeName: string,dataConfig: any ) {return new Promise((resolve, reject) => {if (!db) {reject("數據庫不存在或沒有初始化");}if (!dataConfig || !dataConfig.value) {reject("value是必傳項,參照格式{[keyPath]:'key',value:'value'}");}const req = db.transaction([storeName], "readwrite").objectStore(storeName).put(dataConfig);// 操作成功req.onsuccess = function () {resolve({ code: 0, success: true, data: null, msg: "數據更新成功!" });};// 操作失敗req.onerror = function () {const data = {code: -1,success: false,data: null,msg: "數據更新失敗!",};resolve(data);};}); };????????d.使用promise封裝 get方法
/*** 查詢數據* @param {object} db 數據庫實例* @param {string} storeName 倉庫名稱* @param {string} key 數據主鍵**/ export const getData = function (db: any, storeName: string, key: string) {return new Promise((resolve, reject) => {if (!db) {reject("數據庫不存在或沒有初始化");}const req = db.transaction([storeName], "readonly").objectStore(storeName) // 倉庫對象.get(key);// 操作成功req.onsuccess = function (e: { target: { result: any } }) {resolve({code: 0,success: true,data: e?.target?.result,msg: "數據獲取成功!",});};// 操作失敗req.onerror = function () {const data = {code: -1,success: false,data: null,msg: "數據獲取失敗!",};resolve(data);};}); };????????d.使用promise封delete方法?
/*** 刪除數據* @param {object} db 數據庫實例* @param {string} storeName 倉庫名稱* @param {string} key 數據主鍵**/ export const deleteData = function (db: any, storeName: string, key: string) {return new Promise((resolve, reject) => {if (!db) {reject("數據庫不存在或沒有初始化");}const req = db.transaction([storeName], "readwrite").objectStore(storeName) // 倉庫對象.delete(key);// 操作成功req.onsuccess = function (e: { target: { result: any } }) {resolve({code: 0,success: true,data: e?.target?.result,msg: "數據刪除成功!",});};// 操作失敗req.onerror = function () {const data = {code: -1,success: false,data: null,msg: "數據刪除失敗!",};resolve(data);};}); };????????e.使用promise封裝 游標查詢方法?
/*** 使用游標查詢數據* @param {object} db 數據庫實例* @param {string} storeName 倉庫名稱* @param {string} indexKey 查詢的索引的鍵值* @param {string} index 查詢的索引值**/ export const getIndexData = function (db: any,storeName: string,indexKey: string,index: string ) {return new Promise((resolve, reject) => {if (!db) {reject("數據庫不存在或沒有初始化");}const keyRange = IDBKeyRange.only(index);const req = db.transaction([storeName], "readonly").objectStore(storeName) // 倉庫對象.index(indexKey).openCursor(keyRange, "next");// 操作成功req.onsuccess = function (e: { target: { result: any } }) {resolve({code: 0,success: true,data: e?.target?.result,msg: "數據查詢成功!",});};// 操作失敗req.onerror = function () {const data = {code: -1,success: false,data: null,msg: "數據查詢失敗!",};resolve(data);};}); };????????詳細封裝文件查看請戳鏈接:
https://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsxhttps://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsx
indexDB常見錯誤:
????????1.transaction出錯
Uncaught DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: A version change transaction is running.
????????錯誤原因:
????????有一個事務在運行,不能打開第二個事務.
????????解決辦法:
????????將transaction相關的代碼寫在objectStore.transaction.oncomplete中
var request = indexedDB.open('sql', 2); request.onupgradeneeded = function(event) {var db = event.target.result;var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });objectStore.transaction.oncomplete = function(event) {var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers").add({ id: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" });}; };????????2.Cannot read錯誤
Uncaught TypeError: Cannot read property ‘xxx’ of undefined
? ? ? ? 錯誤原因:
????????因為indexDB代碼都是異步代碼,所以有些還沒有定義部分的代碼可能會優先執行,導致這個變量為undefined
? ? ? ? 3.嘗試讀取數據時候報錯
Uncaught InvalidStateError: Failed to read the ‘result’ property from ‘IDBRequest’: The request has not finished
????????錯誤示范:
var r = indexedDB.open(); var db = null; r.onsuccess = function(event) { db = event.target.result); }????????正確示范:
var r = indexedDB.open(); r.onsuccess = function(event) {var db = event.target.result; };????????4.Put的時候報錯
failed to execute ‘put’ on ‘idbobjectstore’ evaluating the object store’s key path did not yield a value
? ? ? ? 錯誤原因:????????
????????儲存數據的時候必須要帶上object store的key一起儲存,或者使用一個key:generator({autoIncrement: true})
? ? ? ? 示例:
var store = db.createObjectStore('my_store', {keyPath: 'key'}); store.put({key: 11, value: 33}); // OK store.put({value: 66}); // throws, since 'key' is not present var store = db.createObjectStore('my_store', {keyPath: 'key', autoIncrement: true}); store.put({key: 11, value: 33}); // OK, key generator set to 11 store.put({value: 66}); // OK, will have auto-generated key 12????????5.createObjectStore undefined
Cannot read property ‘createObjectStore’ of undefined
? ? ? ?錯誤原因:???
????????這是因為indexedDB是異步的,你必須在回調函數中使用createObjectStore方法,即使你把createObjectStore調用寫在open函數后面,也無法保證哪個先完成。
? ? ? ?6.Failed to execute ‘createObjectStore’
Failed to execute ‘createObjectStore’ on ‘IDBDatabase’: The database is not running a version change transaction.
? ? ? ?錯誤原因:???????????
????????這是由于在success事件的回調中調用createObjectStore方法,該方法應該在upgradeneeded事件的回調中調用。
????????7.stores was not found.
Failed to exectue ‘transaction’ on ‘IDBDatabase’: One of the specified stores was not found.
????????錯誤原因:??????????
????????這是因為upgradeneeded事件沒有被觸發。
????????這里需要注意upgradeneeded事件。首先,根據API,應該在upgradneeded事件的回調函數中調用createObjectStore方法創建store object,不應該在success的回調中,否則會報錯。其次,當為open方法傳入一個本域沒有的數據庫名時,會創建相應的數據庫,并觸發success、upgradeneeded事件,從而創建一個store object。但是,chrome54并不會觸發upgradeneeded事件,造成store object不會被創建,后續在store object上創建事務并操作數據時候就會報錯。Stackoverflow上提供的解決辦法是,在open方法傳入第二個參數(與已有version不同,且更大),這樣就會觸發chrome上的upgradeneeded事件了。不過,每次都需要調用db.version獲取當前的版本號。
????????8.add出現錯誤
Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.
? ? ? ? 錯誤原因:
????????transaction結束了,不能繼續使用,所以才需要每次操作數據庫都要獲得一個
其他
????????IDBOpenDBRequest還有一個類似回調函數句柄——onupgradeneeded。
????????該句柄在我們請求打開的數據庫的版本號和已經存在的數據庫版本號不一致的時候調用。
????????indexedDB.open方法還有第二個可選參數,數據庫版本號,數據庫創建的時候默認版本號為1,當我們傳入的版本號和數據庫當前版本號不一致的時候onupgradeneeded就會被調用,當然我們不能試圖打開比當前數據庫版本低的version.
????????代碼中定義了一個myDB對象,在創建indexedDB request的成功毀掉函數中,把request獲取的DB對象賦值給了myDB的db屬性,這樣就可以使用myDB.db來訪問創建的indexedDB了。
????????用indexedBD的時候要善用onerror來獲取錯誤的信息,這樣就知道哪里出錯了。
參考文章鏈接:
- 前端indexDB數據庫的使用 - 簡書
- HTML5本地數據庫--IndexDB的基本操作_ZacheryWu的博客-CSDN博客_indexdb 操作
- indexDB入門_小白目的博客-CSDN博客_indexdb
- IndexedBD的一些心得(總結)_PkJY的博客-CSDN博客
- HTML5-indexedDB使用常見錯誤總結_柒青衿的博客-CSDN博客
- indexedDB基本使用(內含案例)_小小白號的博客-CSDN博客_indexeddb使用
- indexedDB常見坑人錯誤_st紫月的博客-CSDN博客
- indexedDB學習小結_黑月DM的博客-CSDN博客
- indexedDB 小結_chui62219603的博客-CSDN博客
- indexDB入門到精通,indexdb增刪查改,封裝indexdb類庫,indexdb基本使用_淋雨一直走~的博客-CSDN博客_indexdb
- indexDB出坑指南 | enne5w4
總結
以上是生活随笔為你收集整理的前端数据库indexedDB入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华盛顿合作规律
- 下一篇: python加注释_python中加注释