外國網(wǎng)站學(xué)習(xí)做任務(wù) 升級互聯(lián)網(wǎng)推廣的方式
連接數(shù)據(jù)庫
我們首先創(chuàng)建一個(gè)DBManager類,通過這個(gè)類new出來的對象管理一個(gè)數(shù)據(jù)庫
具體關(guān)于indexedDB的相關(guān)內(nèi)容可以看我的這篇博客
indexedDB
class DBManager{}
我們首先需要打開數(shù)據(jù)庫,打開數(shù)據(jù)庫需要數(shù)據(jù)庫名和該數(shù)據(jù)庫的版本
constructor(dbName, version) {this.dbName = dbName;this.version = version;this.db = null
}
在constructor中我們先初始化數(shù)據(jù)庫相關(guān)信息,dbName為該對象管理的數(shù)據(jù)庫的數(shù)據(jù)庫名,version為該數(shù)據(jù)庫的版本,db為該數(shù)據(jù)庫的IDBDatabase對象
現(xiàn)在我們開始實(shí)現(xiàn)openDB方法
openDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('數(shù)據(jù)庫打開成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('數(shù)據(jù)庫打開失敗')reject(event.target.error)}})
}
因?yàn)榇蜷_數(shù)據(jù)庫涉及i/o操作,所以是異步的,所以我們需要返回一個(gè)Promise
關(guān)閉數(shù)據(jù)庫
當(dāng)數(shù)據(jù)庫使用完畢,為了節(jié)省資源,我們可以選擇斷開數(shù)據(jù)庫的連接
closeDB() {if (this.db) {console.log('關(guān)閉數(shù)據(jù)庫')this.db.close()this.db = null}
}
刪除數(shù)據(jù)庫
如果數(shù)據(jù)庫某一天不在使用,我們可以選擇刪除這個(gè)數(shù)據(jù)庫來節(jié)省資源
deleteDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.deleteDatabase(this.dbName)cmd.onsuccess = (event) => {console.log('數(shù)據(jù)庫刪除成功')resolve()}cmd.onerror = (event) => {console.log('數(shù)據(jù)庫刪除失敗')reject(event.target.error)}})
}
同樣,刪除數(shù)據(jù)庫是異步的,我們需要返回一個(gè)Promise
我們接下來來測試一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.closeDB()await db.deleteDB()
})()
需要注意的是,我們在刪除數(shù)據(jù)庫之前必須先斷開數(shù)據(jù)庫連接
創(chuàng)建對象倉庫
我們接下來需要實(shí)現(xiàn)創(chuàng)建對象的方法
createStore(storeName, keyPath, keys) {return new Promise((resolve, reject) => {if (this.db) {console.log('添加存儲(chǔ)倉庫', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}resolve(this.db)} else {reject('數(shù)據(jù)庫未打開')}})
}
但是如果我們直接通過調(diào)用createStore來創(chuàng)建對象倉庫的話瀏覽器會(huì)報(bào)錯(cuò)
這是因?yàn)獒槍ο髠}庫的操作是需要放在db.onupgradeneeded的回調(diào)中,所以我們不能直接這么寫
數(shù)據(jù)庫的更新
我們可以用一個(gè)更新方法來手動(dòng)觸發(fā)onupgradeneeded這個(gè)事件
updateDB(callback) {return new Promise(async (resolve, reject) => {console.log('數(shù)據(jù)庫升級')if (this.db) {this.closeDB()this.version += 1await this.openDB(callback)resolve(this.db)}else {reject('數(shù)據(jù)庫未打開')}})
}
openDB(callback) {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('數(shù)據(jù)庫打開成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('數(shù)據(jù)庫打開失敗')reject(event.target.error)}if (callback) {cmd.onupgradeneeded = (event) => {this.db = event.target.resultcallback(event)}}})
}
update方法通過調(diào)用close和open方法更新數(shù)據(jù)庫,同時(shí)將對對象倉庫的操作封裝成函數(shù)傳入update方法中,再將這個(gè)函數(shù)放入open方法中,open方法中通過判斷是否傳入?yún)?shù)來判斷是否需要監(jiān)聽onupgradeneeded事件,因?yàn)楫?dāng)用戶第一次創(chuàng)建數(shù)據(jù)庫的時(shí)候會(huì)觸發(fā)這個(gè)事件,而第一次的時(shí)候我們是不需要監(jiān)聽的
接下來我們重新處理下createStore里的邏輯
createStore(storeName, keyPath, keys) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('添加存儲(chǔ)倉庫', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}})resolve(this.db)} else {reject('數(shù)據(jù)庫未打開')}})
}
接下來我們再來測試一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.closeDB()await db.deleteDB()
})()
為什么是先打印添加存儲(chǔ)倉庫,后打印數(shù)據(jù)庫打開?因?yàn)楫?dāng)IDBDatabase對象同時(shí)出發(fā)onsuccess和onupgradeneeded事件時(shí),會(huì)先執(zhí)行onupgradeneeded的回調(diào),然后執(zhí)行onsuccess中的回調(diào)
數(shù)據(jù)記錄的操作
我們接下來實(shí)現(xiàn)關(guān)于數(shù)據(jù)記錄的方法
增加數(shù)據(jù)
增加數(shù)據(jù)記錄的邏輯較為簡單,調(diào)用indexedDB提供的add方法就行
insert(storeName, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.add(data)cmd.onsuccess = (event) => {console.log('插入數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('插入數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})
}
我們來測試一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.insert("student", { id: 1, name: "張三", age: 18, score: 90 })await db.insert("student", { id: 2, name: "李四", age: 20, score: 56 })await db.closeDB()await db.deleteDB()
})()
查詢數(shù)據(jù)
查詢數(shù)據(jù)我們需要根據(jù)不同的查詢方式來實(shí)現(xiàn)不同的方法
-
通過key查詢
queryByKey(storeName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.get(value)cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}}) }
-
查詢?nèi)繑?shù)據(jù)記錄
queryAll(storeName) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.getAll()cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}}) }
-
通過游標(biāo)查詢
queryByCursor(storeName, range, direction = "next") {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cursor = range ? store.openCursor(range, direction) : store.openCursor()const result = []cursor.onsuccess = (event) => {const cursor = event.target.resultif (cursor) {result.push(cursor.value)cursor.continue()} else {console.log('查詢數(shù)據(jù)成功')resolve(result)}}cursor.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}}) }
-
通過指定key-value查詢
queryByIndex(storeName, indexName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.index(indexName).get(value)cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}}) }
我們現(xiàn)在來測試一下
(async function () {const db = new DBManager("student", 1)await db.openDB()await db.createStore("student", "id", ['id', 'name', 'age', 'score'])await db.insert("student", { id: 1, name: "張三", age: 18, score: 90 })await db.insert("student", { id: 2, name: "李四", age: 20, score: 56 })await db.insert("student", { id: 3, name: "王五", age: 19, score: 80 })await db.insert("student", { id: 4, name: "趙六", score: 70 })const result = await db.queryByIndex("student", "age", 18)console.log(result)const result2 = await db.queryByKey("student", 3)console.log(result2)const result3 = await db.queryByCursor("student")console.log(result3)const result4 = await db.queryByCursor("student", IDBKeyRange.only(4))console.log(result4)const result5 = await db.queryAll("student")console.log(result5)await db.closeDB()await db.deleteDB()
})()
更新數(shù)據(jù)
更新數(shù)據(jù)記錄也是通過調(diào)用indexedDB中的put方法來實(shí)現(xiàn)
update(storeName, key, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.put(data)cmd.onsuccess = (event) => {console.log('更新數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('更新數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})
}
刪除數(shù)據(jù)
更新數(shù)據(jù)記錄也是通過調(diào)用indexedDB中的delete方法來實(shí)現(xiàn)
delete(storeName, key) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.delete(key)cmd.onsuccess = (event) => {console.log('刪除數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('刪除數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})
}
完整代碼
最后我們來看一下完整代碼
class DBManager {constructor(dbName, version) {this.dbName = dbName;this.version = version;this.db = null}openDB(callback) {return new Promise((resolve, reject) => {const cmd = indexedDB.open(this.dbName, this.version)cmd.onsuccess = (event) => {console.log('數(shù)據(jù)庫打開成功')this.db = event.target.resultresolve(this.db)}cmd.onerror = (event) => {console.log('數(shù)據(jù)庫打開失敗')reject(event.target.error)}if (callback) {cmd.onupgradeneeded = (event) => {this.db = event.target.resultcallback(event)}}})}closeDB() {if (this.db) {console.log('關(guān)閉數(shù)據(jù)庫')this.db.close()this.db = null}}deleteDB() {return new Promise((resolve, reject) => {const cmd = indexedDB.deleteDatabase(this.dbName)cmd.onsuccess = (event) => {console.log('數(shù)據(jù)庫刪除成功')resolve()}cmd.onerror = (event) => {console.log('數(shù)據(jù)庫刪除失敗')reject(event.target.error)}})}updateDB(callback) {return new Promise(async (resolve, reject) => {console.log('數(shù)據(jù)庫升級')if (this.db) {this.closeDB()this.version += 1await this.openDB(callback)resolve(this.db)}else {reject('數(shù)據(jù)庫未打開')}})}createStore(storeName, keyPath, keys) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('添加存儲(chǔ)倉庫', storeName)const store = this.db.createObjectStore(storeName, { keyPath: keyPath, autoIncrement: true })if (keys) {keys.forEach(key => {store.createIndex(key, key, { unique: key === keyPath ? true : false })})}})resolve(this.db)} else {reject('數(shù)據(jù)庫未打開')}})}deleteStore(storeName) {return new Promise(async (resolve, reject) => {if (this.db) {await this.updateDB((event) => {console.log('刪除存儲(chǔ)倉庫', storeName)const store = this.db.deleteObjectStore(storeName)})resolve(this.db)} else {reject('數(shù)據(jù)庫未打開')}})}insert(storeName, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.add(data)cmd.onsuccess = (event) => {console.log('插入數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('插入數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}update(storeName, key, data) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.put(data)cmd.onsuccess = (event) => {console.log('更新數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('更新數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}delete(storeName, key) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readwrite')const store = transaction.objectStore(storeName)const cmd = store.delete(key)cmd.onsuccess = (event) => {console.log('刪除數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('刪除數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}queryByKey(storeName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.get(value)cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}queryAll(storeName) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.getAll()cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}queryByIndex(storeName, indexName, value) {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cmd = store.index(indexName).get(value)cmd.onsuccess = (event) => {console.log('查詢數(shù)據(jù)成功')resolve(event.target.result)}cmd.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}queryByCursor(storeName, range, direction = "next") {return new Promise((resolve, reject) => {if (this.db) {const transaction = this.db.transaction(storeName, 'readonly')const store = transaction.objectStore(storeName)const cursor = range ? store.openCursor(range, direction) : store.openCursor()const result = []cursor.onsuccess = (event) => {const cursor = event.target.resultif (cursor) {result.push(cursor.value)cursor.continue()} else {console.log('查詢數(shù)據(jù)成功')resolve(result)}}cursor.onerror = (event) => {console.log('查詢數(shù)據(jù)失敗')reject(event.target.error)}} else {reject('數(shù)據(jù)庫未打開')}})}
}