iT邦幫忙

2024 iThome 鐵人賽

DAY 26
1
JavaScript

可愛又迷人的 Web API系列 第 26

Day26. 使用 IndexedDB API 做一個筆記儲存功能

  • 分享至 

  • xImage
  •  

上一篇文章有提過,相比 localStorage,IndexedDB 的優勢是不限制儲存的資料量,也能處理大量的結構化資料,因此例如圖像儲存、文件管理 ...等等,使用 IndexedDB 也許是個不錯的選擇。

IndexedDB 與 localStorage 的差異

一開始使用時,大家最常碰到的問題就是,這些功能我用 localStorage 處理就好啦,為什麼還要用 IndexedDB?他除了沒有限制儲存資料量之外,還有什麼差別嗎?這邊整理了一些差異,介紹如下:

  • 資料量和結構:IndexedDB 適合複雜的、大型的資料儲存;localStorage 適合簡單的、小型的 key-value 資料。
  • 搜尋資料的處理方式:IndexedDB 可以通過索引做更有效率的查詢,而 localStorage 只能透過遍歷搜尋資料,效能上也有差。
  • 是否有交易機制:IndexedDB 有交易 (transaction) 機制,確保資料符合 ACID 特性,但 localStorage 沒有。
  • 資料類型:IndexedDB 可以儲存複雜的物件和二進制資料,localStorage 只能儲存字串。

使用 IndexedDB API 做一個筆記儲存功能

讓我們試著用 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>

1. 連接資料庫

使用 indexedDB.open("notesDB", 1) 開啟 notesDB 的資料庫,並將版號設為 1。特別注意的是,如果資料庫不存在,瀏覽器會自動建立,所以不需額外處理。

let db;
const request = window.indexedDB.open("notesDB", 1);

2. 處理開啟資料庫的結果

request 用來處理資料庫開啟、錯誤,成功和升級等事件。

  • onerror:資料庫開啟失敗
  • onsuccess:資料庫開啟成功,呼叫 loadNotes() 載入筆記資料。
request.onerror = function (event) {
  console.error("資料庫建立失敗", event);
};

request.onsuccess = function (event) {
  db = event.target.result;
  console.log("資料庫建立成功");
  loadNotes();
};

3. 初始化資料庫的結構

如果是第一次建立資料庫,或是要升級資料庫版本時,都會觸發 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 });
};

⬇️ 建立好的畫面如下

https://mukiwu.github.io/web-api-demo/img/26-1.png

4. 新增筆記

這段跟前面提到的 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);
  };
}

5. 顯示筆記清單

這部分跟 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);
  };
}

6. 刪除筆記

和 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 的好用之處。有任何問題也歡迎留言討論。


上一篇
Day25. 另一種儲存資料的方式:IndexedDB API
下一篇
Day27. 使用 JavaScript 操作動畫的 Web Animations API
系列文
可愛又迷人的 Web API28
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言