IndexedDB 讓我們透過資料鍵 (key) 儲存、取回物件。所有對資料庫的變更都發生在交易操作;如同大部分網頁儲存方案,IndexedDB 遵守同源政策,也就是說我們只能存取同網域下的資料。
API 有分非同步和同步,非同步 API 適用於大部分情況,包括 Web Worker,而同步 API 應該只用在 Web Worker 內。目前主流瀏覽器尚不支援同步 API,不過即使同步 API 存在,大部分的情況還是以非同步 API 使用為主。
W3C 在 2011/11/18 宣布 WebSQL 已棄用,請用 IndexedDB 作為替代方案。IndexedDB 和 WebSQL 雖然都是儲存方案,但功能並不相同,IndexedDB 是索引資料表資料庫,而 WebSQL 是關聯式資料庫系統。
概念總覽
請將你在其他類型資料庫上的預期從 IndexedDB 上拋開,然後謹記以下重要概念:
- IndexedDB 資料庫儲存資料鍵對資料值的成對資料 (key-value pairs) 。資料值可以是複雜的結構化物件,資料鍵可以是這些物件的屬性,然後我們可以依物件屬性建立索引,進行資料搜尋以及排序。
- IndexedDB 建基於交易性質資料庫模型 (transactional database model) 。任何在 IndexedDB 上進行的操作都是屬於交易 (transaction),IndexedDB API 提供了索引、資料表等等許多物件,然而這些物件都鎖定到某一個特定交易,所以我們不行在交易範疇外執行指令等操作。 交易具有生命週期,所以交易結束後又操作交易會引起例外錯誤,而且交易是自動執行生效的,無法手動執行生效。 想想看如果一個使用者在兩個頁面開啟同一個網路應用程式時,若是沒有交易機制,這兩個頁面對資料庫的存取操作將會產生衝突,因此交易機制是一個相當有用的機制。
- IndexedDB 大部分 API 都是非同步類別。IndexedDB API 不會回傳資料,相反地,我們需要傳入回呼函數 (Callback function) 。我們並非直接、同步地儲存和取得資料,我們是請求資料庫作業,當作業結束時 DOM 事件再通知我們作業結束,然後我們再透過事件類別判斷作業成功或失敗;或許以上作法一開始聽起來有點複雜,不過之所以會如此做是有其背後道理的,而且這種作法和 XMLHttpRequest 相去不遠。
- IndexedDB 透過 DOM 事件通知作業結果。DOM 事件具有一個 typ e屬性 (在 IndexedDB 中,這個屬性幾乎不是 ”success” 就是 ”error” ),DOM 事件還有一個 target 屬性告訴我們事件進行目標,絕大多數情況下,事件的 target 皆為操作資料庫結果產生的 IDBRequest 物件。成功事件不會往上傳播而且無法取消,失敗事件會往上傳播但可取消,請謹記這點,因為正在進行的交易會因為失敗事件而中止,除非取消失敗事件。
- IndexedDB 隨處都在使用請求 (Request) 。接收前述成功或失敗 DOM 事件的物件就是請求,請求有 onsuccess 和 onerror 屬性以及 addEventListener 和 removeEventListener 方法,他們還有 readyState, result 與 errorCode 屬性告訴我們請求狀態,尤其 result 屬性相當奇妙,它可以代表各式不同意義,就看我們是如何建立請求,比如說,一個 IDBCursor 物件或剛存入資料庫的資料的資料鍵。
- IndexedDB 乃是物件導向的。IndexedDB 非關聯式資料庫,沒有資料表,這一點深深影響著應用程式如何設計。 傳統關聯式資料庫中存著由資料列和屬性欄所構成的資料表,另一方面,IndexedDB 存的是某一個資料型態的物件存檔 (Object store),IndexedDB 就只是保存存檔的 Javascript 物件;每一個物件存檔能具備一連串索引用以搜尋和排列資料。
- IndexedDB 沒有結構化查詢語言 (Structured Query Language, SQL) 。IndexedDB 查詢索引以取得指標 (Cursor),然後我們再用指標一一存取查詢結果。
- IndexedDB 遵守同源政策 (Same-origin policy) 。URL 的網域、應用層協定和埠號構成來源,每一個來源皆有其獨特關聯的資料庫,每一個資料庫都有名稱辨識其所在來源。 同源政策限制了 IndexedDB 存取其他來源下的資料,例如來自 http://www.example.com/app/ 的應用能夠存取 http://www.example.com/dir/ 下的資料,因為他們其實來自同一個來源,但無法存取 http://www.example.com:8080/dir/ (埠號不同) 或 https://www.example.com/dir/ (協定不同) 的資料,因為來源不同。
定義
以下是 IndexedDB API 專有名詞定義與解說。
Database
資料庫
資訊集中存放之處,由一或數個物件存檔 (Object store) 組成,每一個資料庫必須具有:
- 名稱。用以辨識資料庫所屬來源,在生命週期間固定不變,可以任意字串 (甚至空白字串) 。
- 目前版本。當資料庫創建後,如未指定,它的版本是整數1。每一個資料庫同一時間只能有一個版本。
物件存檔 (object store)
資料存放於資料庫的機制;物件存檔持有紀錄,也就是資料鍵和資料值的成對紀錄。記錄在物件存檔中依資料鍵 (keys)大小由小到大排列。
每一個物件存檔都具備獨特名稱,物件存檔能夠選擇性地持有 key generator 和 key path;若有 key path 便會採用 in-line keys,否則便採用 out-of-line keys。
版本 (version)
當資料庫創建後,如未指定,它的版本是整數 1;每一個資料庫同一時間只能有一個版本,唯一變更版本的方法是開啟比目前更大的版本,變更版本將啟動 versionchange 交易並且觸發 upgradeneeded 事件,upgradeneeded 事件處理器也是唯一可以變更資料庫結構之處。
注意: 本處解說涵蓋近期大部分規範,舊版瀏覽器支援現在已經廢棄的 IDBDatabase.setVersion() 方法。
database connection
開啟資料庫的作業,一個資料庫同一時間可以擁有多個連線。
交易 (transaction)
在特定資料庫上進行資料存取和資料修改的一連串作業,這便是我們和資料庫的互動,事實上,任何讀取或變更資料庫資料都會發生在交易之中。
只要寫入交易間沒有重疊範疇 (scopes),一個資料庫連結可以同時擁有多個進行中交易。交易範疇在交易創建當下決定,交易範疇定義了與交易互動的物件存檔以及將交易期間不可變更部分,例如說,有一個資料庫連結正在進行寫入交易,這項寫入交易涵蓋 flyingMonkey 物件存檔,我們可以進行第二項有關 unicornCentaur 與 unicornPegasus 物件存檔的交易。至於讀取交易則不受交易範疇重疊限制。
交易的生命應該不可過長,所以瀏覽器可以終止交易時間過長的交易,好讓長時間被鎖定的資料得以解除鎖定;交易可以中止,中止後交易所造成的變更都會被復原;交易的中止不必要等到交易開始或進行中。
交易有三種模式: readwrite, readonly 和 versionchange,只能透過 versionchange 交易創建、刪除物件存檔與索引。
請求 (request)
每一個讀寫作業都是一個請求。
索引 (index)
一個用來查詢其他物件存檔內紀錄的特殊物件存檔,被查詢的物件存檔我們稱為參照物件存檔 (referenced object store) 。索引是資料鍵和值的成對紀錄,其中值就是參照物件存檔內的記錄的資料鍵;當參照物件存檔有紀錄新增、變更或刪除時索引內的紀錄也會相應自動產生,索引內的紀錄只能指向參照物件存檔內的一筆紀錄,但可以有多筆索引紀錄參照同一個物件存檔。物件存檔變更同時,所有的相關索引也會自動相應更新。
除此之外,我們也可以透過資料鍵 (key) 來查詢物件存檔中的紀錄。
資料鍵 (Key) 和資料值 (Value)
資料鍵 (key)
資料存放在物件存檔中。物件存檔有三種方式產生資料鍵: 資料鍵產生器 (key generator)、資料鍵路徑 (key path) 以及指定值。資料鍵的資料型態 (data type) 必須要是能夠進行大小排序;物件存檔中的每一筆紀錄都要有一支獨特的對應資料鍵,不能和同一個物件存檔中其他紀錄的資料鍵相同。
資料鍵可以是: string, date, float和 array (陣列);對於 array,資料鍵範圍可以是空值到無限,還可以在 array 中包含另一個 array,至於以字串或整數作資料鍵則無特殊規定。
除此之外,我們也可以透過索引 (index) 來查詢物件存檔中的紀錄。
資料鍵產生器 (key generator)
自動生成有大小順序關係的資料鍵的機制;因為資料鍵是必要的,所以如果沒有資料鍵或想省去給定資料鍵的步驟,那就需要用產生器自動產生資料鍵。
in-line key
當物件存檔有資料鍵路徑時稱為有使用 in-line key。
out-of-line key
當物件存檔沒有資料鍵路徑時稱為使用 out-of--line key。
資料鍵路徑 (key path)
定義瀏覽器應該如何從物件存檔資料值或索引中提取資料鍵。以下為有效的資料鍵路徑: 一個空字串、一個 Javascript 辨識名稱 (JavaScript identifier) 或數個由”.”(ASCII字元第46字碼) 分隔的 Javascript 辨識名稱。路徑不可含有空白字元。
資料值 (value)
每筆紀錄都有資料值,資料值可以是任意Javascript所能表達的項目,包括boolean, number, string, date, object, array, regexp, undefined 以及 null。
儲存物件或陣列裡的屬性和值也可以是任意Javascript所能表達的項目。
可以儲存 Blobs 和檔案,請參照 W3C 規範。
範圍和範疇
範疇
交易影響到的物件和索引。讀取交易間的範疇可以互相重疊、同時進行,然後寫入交易範疇則不可,如果同時開啟多項交易範疇相同的寫入交易的話,那麼這些交易會排入佇列 (queue),一個接著一個完成後執行。
指標 (Cursor)
在一定資料鍵範圍以內往覆 (iterate) 資料的機制。指標指向了一個物件存檔或索引,它會在一定範圍內,由小到大或由大到小循著紀錄的資料鍵移動,詳細資訊請參照 IDBCursor 或 IDBCursorSync 。
資料鍵範圍
某種資料型態的一段連續區間,這個區間將作為資料鍵的上下限,我們透過資料鍵或資料鍵範圍取出物件存檔和索引值;藉由上下限我們可以做到存取資料鍵介於 x 和 y 之間的資料,詳細請參考 IDBKeyRange 。
限制
IndexedDB 設計上足以滿足大部分客戶端儲存需求,不過,以下情況並不適用 IndexedDB :
- 多國語言排序。各國語言字串的排序不一定都一樣,所以 IndexedDB 無法處理多國語言字串排序。不過雖然 IndexedDB 無法處理多國語言字串排序,我們可以改為先讀出資料後再自行排序。
- 同步化。IndexedDB API 設計上並沒有考慮到和伺服器端資料庫同步化,所以客戶端和伺服器端資料庫同步需要我們自行處理。
- 全文搜尋。IndexedDB 沒有等同於 SQL 的 LIKE 的 API 。
除此之外,請小心瀏覽器可能會清空資料庫,例如:
- 使用者要求清空資料庫。許多瀏覽器讓使用者可以清空網站的 cookies、書籤、密碼和 IndexedDB 資料庫。
- 私密瀏覽。Firefox 和 Chrome 等瀏覽器提供私密瀏覽模式,當瀏覽結束後,資料庫會被清空。
- 超出硬碟空間或資料庫空間上限。
- 資料毀損。
- 當未來有不相容於現在的修改。
瀏覽器確切的狀況和功能範疇未來會變更,但基本上瀏覽器還是會盡可能保存資料。