深入IndexedDB

IndexedDB是多年來瀏覽器引入的存儲功能之一。這是IndexedDB的簡介,IndexedDB是所有現代瀏覽器都支持的Web數據庫

IndexedDB簡介

IndexedDB是多年來瀏覽器引入的存儲功能之一。這是一個鍵/值存儲(一個noSQL數據庫),被認為是在瀏覽器中存儲數據的最終解決方案

這是一個異步API,這意味著執行昂貴的操作不會阻止UI線程,從而為用戶提供草率的體驗。它可以存儲無限量的數據,儘管一旦超過某個閾值,系統會提示用戶為站點提供更高的限制。

它是所有現代瀏覽器均支持

它支持事務,版本控制並提供良好的性能。

在瀏覽器內部,我們還可以使用:

  • 餅乾:可以容納非常少量的字符串
  • 網絡存儲(或DOM存儲),該術語通常標識localStorage和sessionStorage這兩個鍵/值存儲。 sessionStorage,不保留數據,該數據在會話結束時被清除,而localStorage在各個會話之間保留數據

本地/會話存儲的缺點是上限較小(且不一致),每個站點的瀏覽器實現提供2MB到10MB的空間。

過去我們也有Web SQL,是SQLite的包裝器,但是現在這是不推薦使用並且在某些現代瀏覽器上不受支持,因此它從未被公認為標準,因此不應使用,儘管83%的用戶已在其設備上使用了此技術根據我可以使用

從技術上講,您可以在每個站點上創建多個數據庫,但是通常創建一個數據庫,然後您可以在該數據庫中創建多個對象存儲

數據庫是域私有,因此任何其他網站都無法訪問其他網站IndexedDB存儲。

每個商店通常包含一組事物,可以是

  • 弦樂
  • 數字
  • 對象
  • 數組
  • 日期

例如,您可能有一個包含帖子的商店,另一個包含評論的商店。

商店包含許多具有唯一密鑰的項目,該唯一密鑰表示識別對象的方式。

您可以使用事務來更改那些商店,方法是執行添加,編輯和刪除操作,並遍歷它們包含的項目。

自問世以來承諾在ES6中,以及隨後的API向使用Promise的轉變中,IndexedDB API似乎有點老套

雖然沒有任何錯誤,但在我將解釋的所有示例中,我將使用IndexedDB承諾庫由Jake Archibald撰寫,它是IndexedDB API之上的一小層,使它更易於使用。

該庫還用於Google Developers網站上有關IndexedDB的所有示例中

創建一個IndexedDB數據庫

最簡單的方法是使用unpkg,方法是將其添加到頁面標題中:

<script type="module">
import { openDB, deleteDB } from 'https://unpkg.com/idb?module'
</script>

在使用IndexedDB API之前,請始終確保檢查瀏覽器是否支持,即使該瀏覽器廣泛可用,也不知道用戶使用的是哪種瀏覽器:

(() => {
  'use strict'

if (!(‘indexedDB’ in window)) { console.warn(‘IndexedDB not supported’) return }

//…IndexedDB code })()

如何創建一個數據庫

使用openDB()

(async () => {
  //...

  const dbName = 'mydbname'
  const storeName = 'store1'
  const version = 1 //versions start at 1

  const db = await openDB(dbName, version, {
    upgrade(db, oldVersion, newVersion, transaction) {
      const store = db.createObjectStore(storeName)
    }
  })
})()

前兩個參數是數據庫名稱和版本。第三個參數是可選的,它是一個包含函數的對象僅當版本號高於當前已安裝的數據庫版本時才調用。在功能主體中,您可以升級數據庫的結構(存儲和索引)。

將數據添加到存儲中

創建商店時添加數據,對其進行初始化

您使用put對象存儲的方法,但是首先我們需要對其的引用,可以從中獲取db.createObjectStore()當我們創建它時。

使用時put,值是第一個參數,鍵是第二個參數。這是因為如果您指定keyPath創建對象存儲庫時,無需在每個put()請求中都輸入鍵名,只需編寫該值即可。

這填充store0一旦我們創建它:

(async () => {
  //...
  const dbName = 'mydbname'
  const storeName = 'store0'
  const version = 1

const db = await openDB(dbName, version,{ upgrade(db, oldVersion, newVersion, transaction) { const store = db.createObjectStore(storeName) store.put(‘Hello world!’, ‘Hello’) } }) })()

使用事務在創建商店後添加數據

要在以後添加項目,您需要創建一個讀/寫交易,以確保數據庫完整性(如果操作失敗,則事務中的所有操作都會回滾,並且狀態返回到已知狀態)。

為此,請使用對dbPromise調用時得到的對象openDB,然後運行:

(async () => {
  //...
  const dbName = 'mydbname'
  const storeName = 'store0'
  const version = 1

const db = await openDB(/* … */)

const tx = db.transaction(storeName, ‘readwrite’) const store = await tx.objectStore(storeName)

const val = ‘hey!’ const key = ‘Hello again’ const value = await store.put(val, key) await tx.done })()

從商店獲取數據

從商店獲取一件物品:get()

const key = 'Hello again'
const item = await db.transaction(storeName).objectStore(storeName).get(key)

從商店獲取所有物品:getAll()

獲取所有存儲的密鑰

const items = await db.transaction(storeName).objectStore(storeName).getAllKeys()

獲取所有存儲的值

const items = await db.transaction(storeName).objectStore(storeName).getAll()

從IndexedDB刪除數據

刪除數據庫,對像庫和數據

刪除整個IndexedDB數據庫

const dbName = 'mydbname'
await deleteDB(dbName)

刪除對象存儲中的數據

我們使用一筆交易:

(async () => {
  //...

  const dbName = 'mydbname'
  const storeName = 'store1'
  const version = 1

const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction) { const store = db.createObjectStore(storeName) } })

const tx = await db.transaction(storeName, ‘readwrite’) const store = await tx.objectStore(storeName)

const key = ‘Hello again’ await store.delete(key) await tx.done })()

從先前版本的數據庫遷移

的第三個(可選)參數openDB()函數是可以包含一個對象的對象upgrade功能僅當版本號高於當前已安裝的數據庫版本時才調用。在該函數體中,您可以升級數據庫的結構(存儲和索引):

const name = 'mydbname'
const version = 1
openDB(name, version, {
  upgrade(db, oldVersion, newVersion, transaction) {
    console.log(oldVersion)
  }
})

在此回調中,您可以檢查用戶從哪個版本更新,並相應地執行一些操作。

您可以使用以下語法從以前的數據庫版本執行遷移

(async () => {
  //...
  const dbName = 'mydbname'
  const storeName = 'store0'
  const version = 1

const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction) { switch (oldVersion) { case 0: // no db created before // a store introduced in version 1 db.createObjectStore(‘store1’) case 1: // a new store in version 2 db.createObjectStore(‘store2’, { keyPath: ‘name’ }) } db.createObjectStore(storeName) } }) })()

唯一鍵

createObjectStore()如您所見case 1接受第二個參數,該參數指示數據庫的索引鍵。當存儲對象時,這非常有用:put()調用不需要第二個參數,但可以只接受值(一個對象),並且鍵將映射到具有該名稱的對象屬性。

索引為您提供了一種稍後通過該特定鍵檢索值的方法,並且該索引必須是唯一的(每個項目必須具有不同的鍵)

可以將鍵設置為自動遞增,因此您無需在客戶端代碼上跟踪它:

db.createObjectStore('notes', { autoIncrement: true })

如果您的值尚未包含唯一鍵(例如,如果您收集的電子郵件地址沒有關聯的名稱),請使用自動遞增。

檢查商店是否存在

您可以通過調用objectStoreNames()方法:

const storeName = 'store1'

if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName) }

從IndexedDB刪除

刪除數據庫,對像庫和數據

刪除數據庫

await deleteDB('mydb')

刪除對象存儲

僅當打開數據庫時,才可以在回調中刪除對象存儲,並且僅當您指定的版本高於當前安裝的版本時,才調用該回調:

const db = await openDB('dogsdb', 2, {
  upgrade(db, oldVersion, newVersion, transaction) {
    switch (oldVersion) {
      case 0: // no db created before
        // a store introduced in version 1
        db.createObjectStore('store1')
      case 1:
        // delete the old store in version 2, create a new one
        db.deleteObjectStore('store1')
        db.createObjectStore('store2')
    }
  }
})

要刪除對象存儲中的數據,請使用事務

const key = 232 //a random key

const db = await openDB(/*...*/)
const tx = await db.transaction('store', 'readwrite')
const store = await tx.objectStore('store')
await store.delete(key)
await tx.complete

還有更多!

這些只是基礎知識。我沒有談論游標和更高級的內容。 IndexedDB還有更多功能,但我希望這能為您提供一個良好的開端。

免費下載我的JavaScript初學者手冊


更多瀏覽器教程: