上一篇文章有提過,相比 localStorage,IndexedDB 的優勢是不限制儲存的資料量,也能處理大量的結構化資料,因此例如圖像儲存、文件管理 ...等等,使用 IndexedDB 也許是個不錯的選擇。
一開始使用時,大家最常碰到的問題就是,這些功能我用 localStorage 處理就好啦,為什麼還要用 IndexedDB?他除了沒有限制儲存資料量之外,還有什麼差別嗎?這邊整理了一些差異,介紹如下:
讓我們試著用 IndexedDB API 做一個簡單的新增 / 刪除筆記功能吧!其實筆記功能也能用 localStorage 做,但可以透過這個範例比較兩者之間的差異。
我的這個筆記功能很簡單,只有一個輸入筆記的框,還有顯示筆記的列表與刪除按鈕,這邊就快速處理 HTML,CSS 不是重點,就不放上來了。
<div class="container">
<form id="note-form">
<textarea id="note-content" placeholder="輸入筆記..."></textarea>
<button type="submit">儲存筆記</button>
</form>
<h2>筆記列表</h2>
<ul id="notes-list"></ul>
</div>
使用 indexedDB.open("notesDB", 1)
開啟 notesDB 的資料庫,並將版號設為 1。特別注意的是,如果資料庫不存在,瀏覽器會自動建立,所以不需額外處理。
let db;
const request = window.indexedDB.open("notesDB", 1);
request
用來處理資料庫開啟、錯誤,成功和升級等事件。
onerror
:資料庫開啟失敗onsuccess
:資料庫開啟成功,呼叫 loadNotes()
載入筆記資料。request.onerror = function (event) {
console.error("資料庫建立失敗", event);
};
request.onsuccess = function (event) {
db = event.target.result;
console.log("資料庫建立成功");
loadNotes();
};
如果是第一次建立資料庫,或是要升級資料庫版本時,都會觸發 onupgradeneeded
,我們使用 db.createObjectStore
建立一個資料表,並將 id
設為主鍵,且自動遞增 (autoIncrement: true
),此外建立 content
的 index,以便根據筆記內容進行查詢。
request.onupgradeneeded = function (event) {
db = event.target.result;
const objectStore = db.createObjectStore("notes", { keyPath: "id", autoIncrement: true });
objectStore.createIndex("content", "content", { unique: false });
};
⬇️ 建立好的畫面如下
這段跟前面提到的 Create 範例類似,使用 db.transaction
建立一個交易,再用 add()
方法將筆記新增到資料庫中。
function addNote(content) {
const transaction = db.transaction(["notes"], "readwrite");
const objectStore = transaction.objectStore("notes");
const request = objectStore.add({ content: content });
request.onsuccess = function () {
loadNotes();
};
request.onerror = function (event) {
console.error("新增筆記失敗", event);
};
}
這部分跟 Read 的範例類似,因為只要讀取資料,所以該交易的 mode 可用 readonly
。另外因為要取得所有的筆記,所以使用 getAll()
方法,不用另外做 index 搜尋。
筆記清單的刪除按鈕,會呼叫 deleteNote()
以刪除筆記,我們下個段落會提到。
function loadNotes() {
const transaction = db.transaction(["notes"], "readonly");
const objectStore = transaction.objectStore("notes");
const request = objectStore.getAll();
request.onsuccess = function (event) {
const notes = event.target.result;
const notesList = document.getElementById("notes-list");
notesList.innerHTML = "";
notes.forEach(note => {
const li = document.createElement("li");
li.textContent = note.content;
const deleteButton = document.createElement("button");
deleteButton.textContent = "刪除";
deleteButton.onclick = function () {
deleteNote(note.id);
};
li.appendChild(deleteButton);
notesList.appendChild(li);
});
};
request.onerror = function (event) {
console.error("載入失敗", event);
};
}
和 Delete 的範例類似,我們可以刪除指定的筆記,先建立一個 mode 為 readwrite
的交易,再用 delete()
方法,根據筆記的 id
刪除對應的資料。
function deleteNote(id) {
const transaction = db.transaction(["notes"], "readwrite");
const objectStore = transaction.objectStore("notes");
const request = objectStore.delete(id);
request.onsuccess = function () {
loadNotes();
};
request.onerror = function (event) {
console.error("刪除失敗", event);
};
}
以下是完整的程式碼,供各位參考
let db;
const request = window.indexedDB.open("notesDB", 1);
request.onerror = function (event) {
console.error("資料庫建立失敗", event);
};
request.onsuccess = function (event) {
db = event.target.result;
console.log("資料庫建立成功");
loadNotes();
};
request.onupgradeneeded = function (event) {
db = event.target.result;
const objectStore = db.createObjectStore("notes", { keyPath: "id", autoIncrement: true });
objectStore.createIndex("content", "content", { unique: false });
};
function addNote(content) {
const transaction = db.transaction(["notes"], "readwrite");
const objectStore = transaction.objectStore("notes");
const request = objectStore.add({ content: content });
request.onsuccess = function () {
loadNotes();
};
request.onerror = function (event) {
console.error("新增筆記失敗", event);
};
}
function loadNotes() {
const transaction = db.transaction(["notes"], "readonly");
const objectStore = transaction.objectStore("notes");
const request = objectStore.getAll();
request.onsuccess = function (event) {
const notes = event.target.result;
const notesList = document.getElementById("notes-list");
notesList.innerHTML = "";
notes.forEach(note => {
const li = document.createElement("li");
li.textContent = note.content;
const deleteButton = document.createElement("button");
deleteButton.textContent = "刪除";
deleteButton.onclick = function () {
deleteNote(note.id);
};
li.appendChild(deleteButton);
notesList.appendChild(li);
});
};
request.onerror = function (event) {
console.error("載入失敗", event);
};
}
function deleteNote(id) {
const transaction = db.transaction(["notes"], "readwrite");
const objectStore = transaction.objectStore("notes");
const request = objectStore.delete(id);
request.onsuccess = function () {
loadNotes();
};
request.onerror = function (event) {
console.error("刪除失敗", event);
};
}
document.getElementById("note-form").onsubmit = function (event) {
event.preventDefault();
const content = document.getElementById("note-content").value;
if (content.trim()) {
addNote(content);
document.getElementById("note-content").value = "";
}
};
這篇文章使用 IndexedDB API 建立了一個簡單的筆記功能,希望能讓大家更了解 IndexedDB API 的好用之處。有任何問題也歡迎留言討論。