indexedDB介绍
indexedDB介紹
原生介紹
indexedDB 是一個前端數據持久化解決方案(即前端緩存),由瀏覽器實現。
0. 兼容性
1.特點
-
基于文件存儲。意味著其容量可達到硬盤可用空間上限
-
非關系型數據庫。意味著擴展或收縮字段一般無須修改數據庫和表結構(除非新增字段用做索引)
-
鍵值對存儲。意味著存取無須字符串轉換過程
-
存儲類型豐富。意味著瀏覽器緩存中不再是只能存字符串了
-
異步: 意味著所有操作都要在回調中進行
2. 數據庫
一組相關業務數據的存儲集合。
創建一個數據庫
window.indexedDB.open(dbName, version)示例:
const dbRequest = window.indexedDB.open('demo', 1);onpen() 方法說明:
-
如果指定的數據庫已經存在,直接連接數據庫,返回request 實例。【因為indexedDB 是異步的,所以它所有的操作都必須建立一個請求(request),請求的結果將被封裝在request實例中返回】
-
如果不存在指定的數據庫,則創建數據庫,然后連接數據庫,返回request 實例。
-
如果傳入的數據庫版本比瀏覽器實際最新的版本低,則會拋出一個錯誤。
3. 表-對象倉庫
某項業務的數據集合,有三種類型
- 對象型倉庫。
- 每次都存入一個對象
- 該對象有一個屬性路徑必須是keyPath
- 如果對象不存在對應的keyPath,會報錯
- 非對象型倉庫
- 專門用來存儲非對象數據
- 不需要傳keyPath
- 混合倉庫
- 存放混合類型的數據
- 會發生對象污染——當你存入一個對象時,如果該對象中并沒有對應的 keyPath,那么,它會在存入時被自動加上這個keyPath
創建表
if (!db.objectStoreNames.contains(tableName)){db.createObjectStore(tableName, options) }示例:
const dbRequest = window.indexedDB.open('demo', 2);dbRequest.onupgradeneeded = (e) => {const db = e.target.result;if (!db.objectStoreNames.contains('objectStore')) {db.createObjectStore('objectStore', { keyPath: 'id' });}if (!db.objectStoreNames.contains('objectStoreCopy')) {db.createObjectStore('objectStoreCopy', { keyPath: 'id' });}if (!db.objectStoreNames.contains('notObjectStore')) {db.createObjectStore('notObjectStore', { autoIncrement: true });}if (!db.objectStoreNames.contains('mixStore')) {db.createObjectStore('mixStore', { autoIncrement: true, keyPath: 'id'});}}createObjectStore 方法說明:
-
只能在db-request的onupgradeneeded事件中進行,而onupgradeneeded 事件只在數據庫版本升級時觸發,所以我們這里版本號升級了。對于庫的版本說明,見下節。
-
options參數有兩個可設置屬性,見 5.記錄 一節
-
keyPath
-
autoIncrement
上圖所示是設置了keyPath的表,我們看到在Key后面有一個括號里,標識了它的Key path
下圖則是設置了autoIncrement的表,我們看到它只有Key:
-
4. 庫版本
-
一個數據庫同一時間只能存在一個最新的版本(該版本記錄了當前使用的數據庫和表結構)
-
只有在修改數據庫結構和表結構時,版本才需要升級
-
修改數據庫結構和表結構或升級數據庫版本對數據庫內的數據一般沒有影響(除非刪除表)
-
最小版本是:1
5. 記錄
-
一條記錄就是一個鍵值對
-
鍵
- keyPath。在值對象中,獲取一個節點值的屬性鏈式方法的字符串表達
- 自動生成。將沒有keyPath,只有自增的key
-
值
- 字符串
- 日期類型
- 對象
- 數組
- 文件
- Blob
- 圖像數據
- ArrayBuffer
- 無法存儲function等非結構化數據
6. 事務
- 所有記錄的增刪改查都要在事務中進行
- 之所以引入事務,是為了保證操作順序和可靠性
- 順序:事物中所有的操作必須排隊進行
- 可靠性: 在【同一個事務】中,對于【同一張表】的一組操作有一個失敗,之前的都回滾
- 事務的生命周期:事務會把你在它生命周期里規定的操作全部執行,執行完畢,事務就關閉了,無法利用事務實例進行下一步操作
創建事務
db.transaction(objectStoresArray, readWriteMode)示例:
const request = window.indexedDB.open('demo', 2);request.onsuccess = (e) => {const db = e.target.result;let transcation = db.transaction(['objectStore', 'objectStoreCopy', 'notObjectStore', 'mixStore'], 'readwrite');}transaction()方法說明:
-
事務必須在db-request的成功回調onsuccess方法中創建,另注意:數據庫實例db 需要從成功回調的結果的target.result中獲取。
-
兩個參數:
-
objectStoresArray, 一個數組,包含了當前事務中要操作的所有表的名稱
-
readWriteMode: 本次操作是只讀操作還是讀寫操作
- readonly: 只讀
- readwrite:讀寫
-
讀取表
transaction.objectStore(tableName)示例:
// 省略連接數據庫和讀取數據庫實例的過程,以下代碼在dbRquest的回調中進行 let transcation = db.transaction(['objectStore', 'objectStoreCopy', 'notObjectStore'], 'readonly');let os = transcation.objectStore('objectStore');let osc = transcation.objectStore('objectStoreCopy');let nos = transcation.objectStore('notObjectStore');let ms = transcation.objectStore('mixStore');objectStore()方法說明:
-
傳入需要操作的表的名稱。
-
傳入的表名稱必須在transaction()方法的第一個參數中指定,否則將會報錯,比如最后一行讀取的就是一個沒有指定的表,將會報以下錯誤:
添加記錄
objectStore.add(object)示例:
const request = window.indexedDB.open('demo', 2);request.onsuccess = (e) => {const db = e.target.result;let transcation = db.transaction(['objectStore', 'objectStoreCopy', 'notObjectStore','mixStore'], 'readwrite');let os = transcation.objectStore('objectStore');let osc = transcation.objectStore('objectStoreCopy');let nos = transcation.objectStore('notObjectStore');let ms = transcation.objectStore('mixStore');// 對象型倉庫,keyPath為對象的一個屬性os.add({id: 1,name: '張三',sex: '男',other:{ age: 18}});// 非對象型倉庫,存入幾個非對象數據// Date類型const date = new Date();// Blob類型const s = "<div>Hello World!!</div>";const blob = new Blob([s], {type: 'text/xml'});// ArrayBufferconst buffer = new ArrayBuffer(8);// 數組const arr = [1,2,3];// 圖像數據const imageData = new ImageData(100, 100);// 文件const file = new File(["foo"], "foo.txt", {type: "text/plain",});nos.add(date);nos.add(blob);nos.add(buffer);nos.add(arr);nos.add(imageData);nos.add(file);// 混合型倉庫ms.add({id: 1,name: '張三',sex: '男',other:{ age: 18}});ms.add(blob);ms.add(buffer);ms.add(arr);// 對象型倉庫,keyPath 不是對象的屬性,將添加失敗osc.add({name: '張三',sex: '男',other:{ age: 18}});看一下添加結果:
添加失敗與事務回滾
當我們試圖插入一條keyPath與已有記錄的keyPath相同的記錄時,將會失敗,如果同一張表還有其他操作,將隨事務回滾也一起失敗。
let transcation = db.transaction(['objectStore'], 'readwrite');let os = transcation.objectStore('objectStore');// 試圖插入一個主鍵不同的數據let rs3 = os.add({id: 2,name: '張四',sex: '女',other:{ age: 18}});rs3.onsuccess = e => {console.log('rs3成功');};rs3.onerror = e => {console.log('rs3失敗');console.log(e.target.error.message);};// 視圖插入一個主鍵相同的數據let rs = os.add({id: 1,name: '張四',sex: '女',other:{ age: 18}});rs.onsuccess = e => {console.log('rs成功');};rs.onerror = e => {console.log('rs失敗');console.log(e.target.error.message);};我們看到,這里試圖插入一個與已有記錄keyPath不同的記錄,實際上顯示成功了,而嘗試插入一條與已有記錄keyPath 相同的記錄時,提示失敗了,然后我們看數據庫:
雖然id為2的數據提示插入成功了,但是數據里并沒有,說明因為它之后進行的rs失敗了,所以導致事務回滾了,它本來成功的操作也被回滾,最終數據沒有插入進去。
更新記錄
objectStore.put(object)示例:
// 更新一個記錄let rs2 = os.put({id: 1,name: '張四',sex: '女',other:{ age: 18}});rs2.onsuccess = e => {console.log('rs2成功');};rs2.onerror = e => {console.log('rs2失敗');console.log(e.target.error.message);};我們將之前添加到對象型倉庫里id為1的記錄的名字由張三改為張四,性別由男改為女,看看結果:
可以看到,更新成功了
更新一條keyPath不存在的數據:
let rs4 = os.put({id: 2,name: '張三三',sex: '男',other:{ age: 18}});rs4.onsuccess = e => {console.log('rs4成功');};rs4.onerror = e => {console.log('rs4失敗');console.log(e.target.error.message);};可以看到,更新操作如果更新的是一條keyPath不存在的記錄,它將按照新增add() 來處理。
所以:
我們強烈建議,添加數據都使用put()操作
讀取記錄
objectStore.get(KeyPathValue)示例:
const db = e.target.result;let transcation = db.transaction(['objectStore'], 'readwrite');let os = transcation.objectStore('objectStore');// 先插入幾條數據os.put({id: 3,name: '王五',sex: '男',other:{ age: 16}});os.put({id: 4,name: '王六',sex: '女',other:{ age: 16}});os.put({id: 5,name: '鬼腳七',sex: '男',other:{ age: 16}});let rs = os.get(5);rs.onsuccess = e => {console.log('rs成功');console.log(e.target.result);};rs.onerror = e => {console.log('rs失敗');console.log(e.target.error.message);};看看結果
刪除記錄
objectStore.delete(keyPathValue)示例
let transcation = db.transaction(['objectStore'], 'readwrite');let os = transcation.objectStore('objectStore');let rs = os.delete(3);rs.onsuccess = e => {console.log('rs成功');console.log(e.target.result);};rs.onerror = e => {console.log('rs失敗');console.log(e.target.error.message);};7. 索引
- 索引是一個特殊的表
- 索引是對查詢條件的補充
- 這個表有兩個鍵
- 一個是主鍵
- 一個是索引鍵
- 索引倉庫是以索引鍵為鍵對表中記錄的重新組織
- 一個表可以有多個索引
創建索引
objectStore.createIndex(indexName, Path, options)參數說明
-
indexName: 索引名稱
-
Path:? 索引在對象中的路徑
-
options: 可選參數對象
- unique。如果為true,索引將不允許單個鍵的值重復。
- multiEntry 。如果為 true,則當 Path 解析為數組時,索引將為每個數組元素在索引中添加一個條目。 如果為 false,它將添加一個包含數組的條目。
- locale。目前只有Firefox(43+)支持,這允許您為索引指定區域設置
示例:
const request = window.indexedDB.open('demo', 3); request.onupgradeneeded = (e) => {const db = e.target.resultlet os;if (!db.objectStoreNames.contains('objectStore')) {os = db.createObjectStore('objectStore', { keyPath: 'id' });} else {os = e.target.transaction.objectStore('objectStore');}os.createIndex('sex', 'sex', { unique: false });os.createIndex('age', 'other.age', { unique: false });os.createIndex('name', 'name', { unique: true });}- 創建(刪除和修改)索引的操作必須在db的onupgradeneeded 中進行,這表示要對數據庫升級,所以我們又升了版本號。
看看結果:
更具索引查詢記錄
objectStore.index(indexName)示例:
const request = window.indexedDB.open('demo', 3);request.onsuccess = (e) => {const db = e.target.result;let transcation = db.transaction(['objectStore'], 'readwrite');let os = transcation.objectStore('objectStore');let objIndexName = os.index('name');let objIndexAge = os.index('age');let objIndexSex = os.index('sex');let rs1 = objIndexName.get('張三');rs1.onsuccess = e => {console.log('rs1查詢成功');console.log(e.target.result);}rs1.onerror = e => {console.log('rs1查詢失敗');console.log(e.target.error.message);}let rs2 = objIndexAge.get(16);rs2.onsuccess = e => {console.log('rs2查詢成功');console.log(e.target.result);}rs2.onerror = e => {console.log('rs2查詢失敗');console.log(e.target.error.message);}let rs3 = objIndexSex.get('男');rs3.onsuccess = e => {console.log('rs3查詢成功');console.log(e.target.result);}rs3.onerror = e => {console.log('rs3查詢失敗');console.log(e.target.error.message);}}看看結果:
可以看到,上例中我們以索引名稱查詢,
- 第一個查詢由于沒有name為張三的用戶,所以返回為undefined
- 第二個和第三個查詢,分別以age與sex來查詢,都查到了相應的結果
- 但是,查詢到的結果都只有一條,但是我們前面看到,age為16和sex為男的記錄都不止一條
- 這是因為get()操作只返回符合條件的第一條記錄,要獲得所有符合條件的記錄,就需要下面要將的游標。
8. 游標
一個可以遍歷整個表的接口。
建立游標
index.openCursor()移動游標
cursor.continue();示例:
const request = window.indexedDB.open('demo', 3);request.onsuccess = (e) => {const db = e.target.result;let transcation = db.transaction(['objectStore'], 'readwrite');let os = transcation.objectStore('objectStore');let objIndexSex = os.index('sex');let rs1 = objIndexSex.openCursor();let results = []; // 用來存放這個表中所有記錄,它的聲明必須放在 onsuccess 回調函數的外部,因為該回調函數會在遍歷過程中反復執行let resultsMan = []; // 用來存放男人rs1.onsuccess = e => {console.log('rs1查詢成功');let cursor = e.target.result;if (cursor) {results.push(cursor.value);if (cursor.value.sex === '男') {resultsMan.push(cursor.value);}cursor.continue();} else {console.log(results);console.log(resultsMan);}}rs1.onerror = e => {console.log('rs1查詢失敗');console.log(e.target.error.message);}}看看結果:
刪除索引
objectStore.deleteIndex(indexName)示例:
const request = window.indexedDB.open('demo', 4);request.onupgradeneeded = (e) => {const db = e.target.resultlet os;if (!db.objectStoreNames.contains('objectStore')) {os = db.createObjectStore('objectStore', { keyPath: 'id' });} else {os = e.target.transaction.objectStore('objectStore');}os.deleteIndex('sex');}總結
以上是生活随笔為你收集整理的indexedDB介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 刻录系统盘实战学习
- 下一篇: GPS坐标转换(一)-基础知识