IndexedDB 基本使用

tags: Web HTML5 JavaScript IndexedDB
category: Front-End
description: HTML5 IndexedDB 基本使用
created_at: 2021/08/02 00:00:00

cover image

事前準備

  • 處理過 JavaScript 事件
  • 大概知道一些資料庫的知識

前言

最近想說如果有空閒時間就來拿技能競賽題目練一下手,發現說今年題目要求使用 IndexedDB,所以想說就來記錄一下一些筆記。


目標

  • IndexedDB 功能稍微摸過一遍

開啟 Database

indexedDB.open(資料庫名稱, 版本)
  • 這個東西回傳的不是 database 而是 request,要在從 request 去取得 database
  • 版本的型態是 unsigned long long,也就是說非負整數

IDBOpenDBRequest

把他展開來看 應該是類似這樣的東西 IndexedDB Open Database Request

這個東西主要就四個事件需要處理

  • onsuccess
  • onerror
  • onblocked
  • onupgradeneeded

onsuccess

一切正常就會觸發,可以在裡面取到 database

const dbRequest = indexedDB.open(dbName, version)
dbRequest.onsuccess = e => {
    const db = e.target.result
    console.log(db)
}

這樣你就會拿到一個 IDBDatabase 的物件,有一個比較重要的事件 onversionchange,當資料庫的版本發生改變的時候該做的事情,通常是把資料庫連接 close

所以會變成下面這樣

const dbRequest = indexedDB.open(dbName, version)
dbRequest.onsuccess = e => {
    const db = e.target.result
    db.onversionchange = e => {
        console.log('versionchange', e)
        db.close()
    }
}

加入一個 console.log() 可以知道他有沒有被觸發(之後會用到)。


onerror

這...應該很常見了..略


onblocked

上面有提到說 onversionchange 事件,這個是當你有多個頁籤,新的頁籤的資料庫需要升級,但是舊的頁籤還存在資料庫連線,就會進入到這邊(被阻擋block),所以可能就是提醒使用者說請關閉其他頁籤(?)


onupgradeneeded

這裡是資料庫版本發生變化的時候會觸發的事件,在還沒 Open 過,預設的版本是0,所以你版本不管填多少都會觸發進到這個事件(版本填0會被擋住,所以一定得>0)。

注意: 這裡也是你唯一可以修改資料庫結構的地方

dbRequest.onupgradeneeded = e => {
    console.log('upgradeneeded', e)
    const db = e.target.result
    const store = db.createObjectStore(storeName, options)
}

這樣你就可以在你的資料庫裡面建立一個 store,大概可以想像成資料表(table)

- createObjectStore options

  • keyPath: 主鍵 Primary Key 存在 value 物件中的路徑
  • autoIncrement: Key 自動遞增,預設 false

設定 Index

store.createIndex(indexName, keyPath, options)

options 當中比較常用的是 unique,如果設定為 true 將不允許重複

另外也有 multiEntry 可以設定。

  • true: 如果 keyPath 的資料是 Array,會幫你攤平(方便搜尋),有興趣可以自己點進去看看發生什麼事
  • false: 預設

新增預設資料

dbRequest.onupgradeneeded = e => {
    const users = [{
        name: 'user01'
    }, {
        name: 'user02'
    }, {
        name: 'user03'
    }]

    const db = e.target.result
    const store = db.createObjectStore('users', {
        keyPath: 'id',
        autoIncrement: true
    })

    users.forEach(user => {
        store.add(user)
    })
}

這樣就會看到下圖的結果


想取得既有的 store

dbRequest.onupgradeneeded = e => {
    const store = e.target.transaction.objectStore(storeName)
}

在來你就可以繼續對 store 進行操作。


檢查 store 存不存在

db.objectStoreNames.contains(storeName)

操作 Database

操作上都是基於 transaction 的,所以要先取得 transaction,看你要對哪些 store 進行操作。

const transaction = db.transaction(storeNames, mode, options)
  • storeNames: 可以是一個值,或是一個陣列
  • mode: readonly or readwrite,預設為 readonly
  • options:
    • durability:
      • default: 預設值
      • relaxed: 提供比較好的性能,適合用在臨時的資料,ex: 快取
      • strict: 性能比較不好,但比較不會掉資料

比較常用到的事件 oncomplete , 當整個交易(transaction)完成會觸發


從 transaction 取得 store

const store = transaction.objectStore(storeName)

新增資料

const storeRequest = store.add(object)

實際上 store.add() 也是會回來一個 Request 的物件(IDBRequest),主要用的事件是 onsuccessonerroronerror自然不用說,而onsuccess則是執行成功會觸發。

另外也有 store.put() 方法,跟 add() 很像,只差在於說如果 Key 已經存在, add() 會觸發錯誤 DOMException: Key already exists in the object store.,而 put() 則會蓋過去。


刪除資料

const storeRequest = store.delete(key)

取得資料

const request = store.get(id)
request.onsuccess = e => {
    console.log(e.target.result)
}

列出所有資料

需要使用指標 Cursor

store.openCursor().onsuccess = e => {
    const cursor = e.target.result
    if (!cursor) return
    console.log(cursor.key, cursor.value)
    cursor.continue()
}

查詢資料

不論要從 Key 還是 Index 查詢,都可以用 IDBKeyRange 這個物件產生東西的來做 Query

MDN 上面有一張表格非常的詳細

有很多種就不一一舉例了,假設這裡範例使用 only 做查詢,假設 value 是你要查的東西

從 Key 查詢

const value = '...'
const keyRange = IDBKeyRange.only(value)
store.openCursor(keyRange).onsuccess = e => {
    const cursor = e.target.result
    if (!cursor) return
    console.log(cursor.key, cursor.value)
    cursor.continue()
}

從 Index 查詢

const value = '...'
const keyRange = IDBKeyRange.only(value)
store.index(indexName).openCursor(keyRange).onsuccess = e => {
    const cursor = e.target.result
    if (!cursor) return
    console.log(cursor.key, cursor.value)
    cursor.continue()
}

基本上就只差在有沒有 index() 這個函數。


更新及刪除資料

在你使用 Cursor 查詢到資料後,可以利用 Cursor 來做 UpdateDelete

cursor.update({
    ...cursor.value, // 展開舊有資料,如果你還需要
    newKey: newValue, // 新資料
})

或是刪除

cursor.delete()

兩者回來的東西一樣是 IDBRequest,可以再繼續做處理。


總結

上面把 IndexedDB 基本上會用到的東西大概摸了一次,除了資料需要自己稍微做一下以外,其他照著弄應該都可以順利進行。




最後更新時間: 2021年08月02日.