HTML5 的一個重要特性是本地數據持久性,它使用戶能夠在線和離線訪問 Web 應用程序。此外,本地數據持久性使移動應用程序更靈敏,使用的帶寬更少,而且能夠在低帶寬場景中更高效地工作。HTML5 提供了一些本地數據持久性選項。第一個選項是 localstorage,它支持您使用一個簡單的鍵值對來存儲數據。IndexedDB(一個更加強大的選項)支持您本地存儲大量對象,并使用健壯的數據訪問機制檢索數據。
IndexedDB API 取代了 Web Storage API,后者在 HTML5 規范中已不推薦使用。(但一些領先的瀏覽器仍然支持 Web Storage,其中包括蘋果公司的 Safari 和 Opera Web 瀏覽器)與 Web Storage 相比,IndexedDB 具有多個優勢,其中包括索引、事務處理和健壯的查詢功能。本文將通過一系列的示例來展示如何管理 IndexedDB 數據庫。(參見 下載 一節,獲取示例的完整源代碼。)
重要概念
一個網站可能有一個或多個 IndexedDB 數據庫,每個數據庫必須具有惟一的名稱。
一個數據庫可包含一個或多個對象存儲。一個對象存儲(由一個名稱惟一標識)是一個記錄集合。每個記錄有一個鍵 和一個值。該值是一個對象,可擁有一個或多個屬性。鍵可能基于某個鍵生成器,從一個鍵路徑衍生出來,或者是顯式設置。一個鍵生成器自動生成惟一的連續正整數。鍵路徑定義了鍵值的路徑。它可以是單個 JavaScript 標識符或多個由句點分隔的標識符。
規范中包含一個異步 API 和一個同步 API。同步 API 用于 Web 瀏覽器中。異步 API 使用請求和回調。
在以下示例中,輸出附加到一個具有 ID result 的 div 標記上。要更新 result 元素,可在每個數據操作期間清除并設置 innerHTML 屬性。每個示例 JavaScript 函數由 HTML 按鈕的一個 onclick 事件調用。
處理錯誤或異常和調試
所有異步請求都有一個 onsuccess 回調和一個 onerror 回調,前者在數據庫操作成功時調用,后者在一個操作未成功時調用。清單 1 是一個 onerror 回調的示例。
清單 1. 異步錯誤處理函數
request.onerror = function(e) {
// handle error
...
console.log("Database error: " + e.target.errorCode);
};
在使用 IndexedDB API 時,使用 JavaScript try/catch 塊是一個不錯的想法。此功能對處理可能在數據庫操作之前發生的錯誤和異常很有用,比如在數據庫未打開時嘗試讀取或操作數據,或者在另一個讀/寫事務已打開時嘗試寫入數據。
IndexedDB 很難調試和排除故障,因為在許多情況下,錯誤消息是泛泛的,缺乏信息價值。在開發應用程序時,可以使用 console.log 和 JavaScript 調試工具,比如用于 Mozilla Firefox 的 Firebug,或者 Chrome 內置的 Developer Tools。對于任何 JavaScript 密集型應用程序,這些工具的價值是無可估量的,它們尤其適用于使用 IndexedDB API 的 HTML5 應用程序。
使用數據庫
一個數據庫一次只能有一個版本。在首次創建數據庫時,它的初始版本編號為 0。創建數據庫之后,數據庫(和它的對象存儲)只能通過一種稱為 versionchange 的特殊類型的事務來更改。要在創建數據庫后更改它,必須打開具有更高版本的數據庫。此操作會觸發 upgradeneeded 事件。修改數據庫或對象存儲的代碼必須位于 upgradeneeded 事件處理函數中。
清單 2 中的代碼段展示了如何創建數據庫:調用 open 方法并傳遞數據庫名稱。如果不存在具有指定名稱的數據庫,則會創建該數據庫。
清單 2. 創建一個新的數據庫
function createDatabase() {
var openRequest = localDatabase.indexedDB.open(dbName);
openRequest.onerror = function(e) {
console.log("Database error: " + e.target.errorCode);
};
openRequest.onsuccess = function(event) {
console.log("Database created");
localDatabase.db = openRequest.result;
};
openRequest.onupgradeneeded = function (evt) {
...
};
}
要刪除現有數據庫,可以調用 deleteDatabase 方法,并傳遞要刪除的數據庫名稱,如 清單 3 中所示。
清單 3. 刪除現有數據庫
function deleteDatabase() {
var deleteDbRequest = localDatabase.indexedDB.deleteDatabase(dbName);
deleteDbRequest.onsuccess = function (event) {
// database deleted successfully
};
deleteDbRequest.onerror = function (e) {
console.log("Database error: " + e.target.errorCode);
};
}
清單 4 中的代碼段展示了如何打開與現有數據庫的連接。
清單 4. 打開數據庫的最新版本
function openDatabase() {
var openRequest = localDatabase.indexedDB.open("dbName");
openRequest.onerror = function(e) {
console.log("Database error: " + e.target.errorCode);
};
openRequest.onsuccess = function(event) {
localDatabase.db = openRequest.result;
};
}
創建、刪除和打開數據庫就是這么簡單。現在是時候使用對象存儲了。
使用對象存儲
對象存儲是一個數據記錄集合。要在現有數據庫中創建一個新對象存儲,則需要對現有數據庫進行版本控制。為此,請打開要進行版本控制的數據庫。除了數據庫名稱之外,open 方法還接受版本號作為第二個參數。如果希望創建數據庫的一個新版本(也就是說,要創建或修改一個對象存儲),只需打開具有現有數據庫版本更高的數據庫。這會調用 onupgradeneeded 事件處理函數。
要創建一個對象存儲,可以在數據庫對象上調用 createObjectStore 方法,如 清單 5 中所示。
清單 5. 創建對象存儲
function createObjectStore() {
var openRequest = localDatabase.indexedDB.open(dbName, 2);
openRequest.onerror = function(e) {
console.log("Database error: " + e.target.errorCode);
};
openRequest.onsuccess = function(event) {
localDatabase.db = openRequest.result;
};
openRequest.onupgradeneeded = function (evt) {
var employeeStore = evt.currentTarget.result.createObjectStore
("employees", {keyPath: "id"});
};
}
我們已經了解了對象存儲的工作原理。接下來,讓我們看看索引 如何引用包含數據的對象存儲。
使用索引
除了使用鍵來檢索對象存儲中的記錄,還可使用代索引的字段來檢索記錄。對象存儲可具有一個或多個索引。索引是一種特殊的對象存儲,它引用包含數據的對象存儲,在更改所引用的對象存儲時(也就是添加、修改或刪除記錄時)自動更新。
要創建一個索引,必須使用 清單 5 中所示的方法對數據庫進行版本控制。索引可以是惟一的,也可以是不惟一的。惟一索引要求索引中的所有值都是惟一的,比如使用一個電子郵件地址字段。當某個值可以重復出現時,需要使用非惟一索引,比如城市、州或國家。清單 6 中的代碼段展示了如何在 employee 對象的 state 字段上創建一個非惟一索引,在 ZIP code 字段上創建一個非惟一索引,并在 email address 字段上創建一個惟一索引:
清單 6. 創建索引
function createIndex() {
var openRequest = localDatabase.indexedDB.open(dbName, 2);
openRequest.onerror = function(e) {
console.log("Database error: " + e.target.errorCode);
};
openRequest.onsuccess = function(event) {
db = openRequest.result;
};
openRequest.onupgradeneeded = function (evt) {
var employeeStore = evt.currentTarget.result.objectStore("employees");
employeeStore.createIndex("stateIndex", "state", { unique: false });
employeeStore.createIndex("emailIndex", "email", { unique: true });
employeeStore.createIndex("zipCodeIndex", "zip_code", { unique: false })
};
}
接下來,您將使用事務對對象存儲執行一些操作。
使用事務
您需要使用事務在對象存儲上執行所有讀取和寫入操作。類似于關系數據庫中的事務的工作原理,IndexedDB 事務提供了數據庫寫入操作的一個原子集合,這個集合要么完全提交,要么完全不提交。IndexedDB 事務還擁有數據庫操作的一個中止和提交工具。
表 1 列出并描述了 IndexedDB 提供的事務模式。
表 1. IndexedDB 事務模式
默認的事務模式為 readonly。您可在任何給定時刻打開多個并發的 readonly 事務,但只能打開一個 readwrite 事務。出于此原因,只有在數據更新時才考慮使用 readwrite 事務。單獨的(表示不能打開任何其他并發事務)versionchange 事務操作一個數據庫或對象存儲。可以在 onupgradeneeded 事件處理函數中使用 versionchange 事務創建、修改或刪除一個對象存儲,或者將一個索引添加到對象存儲。
要在 readwrite 模式下為 employees 對象存儲創建一個事務,可以使用語句:var transaction = db.transaction("employees", "readwrite");。
清單 7 中的 JavaScript 函數展示了如何使用一個事務,使用鍵來檢索 employees 對象存儲中的一條特定的員工記錄。
清單 7. 使用鍵獲取一個特定的記錄
function fetchEmployee() {
try {
var result = document.getElementById("result");
result.innerHTML = "";
if (localDatabase != null && localDatabase.db != null) {
var store = localDatabase.db.transaction("employees").objectStore("employees");
store.get("E3").onsuccess = function(event) {
var employee = event.target.result;
if (employee == null) {
result.value = "employee not found";
}
else {
var jsonStr = JSON.stringify(employee);
result.innerHTML = jsonStr;
}
};
}
}
catch(e){
console.log(e);
}
}
清單 8 中的 JavaScript 函數展示了如何使用一個事務,以使用 emailIndex 索引而不是對象存儲鍵來檢索 employees 對象存儲中的特定員工記錄。
清單 8. 使用索引獲取特定的記錄
function fetchEmployeeByEmail() {
try {
var result = document.getElementById("result");
result.innerHTML = "";
if (localDatabase != null && localDatabase.db != null) {
var range = IDBKeyRange.only("john.adams@somedomain.com");
var store = localDatabase.db.transaction("employees")
.objectStore("employees");
var index = store.index("emailIndex");
index.get(range).onsuccess = function(evt) {
var employee = evt.target.result;
var jsonStr = JSON.stringify(employee);
result.innerHTML = jsonStr;
};
}
}
catch(e)
【網站聲明】本站除付費源碼經過測試外,其他素材未做測試,不保證完整性,網站上部分源碼僅限學習交流,請勿用于商業用途。如損害你的權益請聯系客服QQ:2655101040 給予處理,謝謝支持。