Javascript 简明教程

JavaScript - IndexedDB

What is IndexedDB?

IndexedDB 或 Indexed Database 表示一个低级别的 JavaScript API。它的功能涉及存储和检索大量结构化数据 - 这包括文件和 blob。利用它使用客户端数据库的能力:它使 web 应用程序能够在用户设备上本地存储、查询和修改数据。该功能在构建面对大量数据的 web 应用程序时尤其有利;在没有互联网连接时仍然能离线操作,并且通过本地数据缓存提供响应的用户体验。

IndexedDB, or Indexed Database, represents a low-level JavaScript API. Its function involves the storage and retrieval of voluminous structured data - this includes files and blobs. With its capabilities to work with client-side databases: it enables web applications to locally store, query, and modify data on the user’s device. This functionality proves particularly advantageous when constructing web applications that confront significant data volumes; operate without an internet connection, working offline and deliver responsive user experiences through local data caching.

IndexedDB 的关键概念和特性:

Key concepts and features of IndexedDB:

  1. Asynchronous API − Relying on event-driven programming, IndexedDB utilizes an asynchronous API to prevent main-thread blocking and enhance user experience.

  2. Database − IndexedDB databases, functioning as containers for object stores, facilitate the organization, retrieval and deletion of data.

  3. Object Store − An object store akin to a table in a relational database, encapsulates collections of JavaScript objects; it provides support for CRUD operations.

  4. Index − Indexes in object stores can enhance the efficiency of data querying, as they are able to improve search and sort performance based on specific fields.

  5. Transactions − Transactions in IndexedDB perform all operations, guaranteeing consistency and integrity through their ability to make multiple actions either succeed or fail as a unit.

Why use IndexedDB?

Web开发者在寻找高效的客户端存储解决方案时,发现IndexedDB必不可少。其异步性质保证了响应式用户体验:它阻止了主线程阻塞;此外,值得称赞的是,它支持通过维护数据完整性来进行事务。IndexedDB能够在本地管理大量结构化数据;这种能力显著增强了离线功能,减少了对频繁服务器通信的需求。凭借其灵活的关键结构、对优化查询的索引支持,以及升级模式的能力;它成为了构建Web应用程序的强大选择。这些应用程序通常需要本地存储、离线支持和最重要的是有效的数据管理。

Web developers, in their search for efficient client-side storage solutions, find IndexedDB indispensable. Its asynchronous nature guarantees a responsive user experience: it thwarts main-thread blocking; moreover, its support for transactions by maintaining data integrity is commendable. IndexedDB is capable of managing significant volumes of structured data locally; this capability significantly enhances offline functionality a reduction on the need for relentless server communication. With its flexible key structure, indexing support for optimized queries, and the ability to upgrade schema; it stands as a robust choice for constructing web applications. These applications often require local storage, offline support and most importantly effective data management.

CRUD Operations

现在让我们看看使用IndexedDB进行CRUD操作的代码。

Let us now see the CRUD operation codes with IndexedDB.

Create/Insert Operation

要在对象存储中插入新记录,可以在IndexedDB中使用add()方法。此方法接受一个JavaScript对象或值作为其参数,然后将其并入指定的object store中。但是,如果已经存在具有相同关键参数的条目;此操作将失败,因此使其成为保证唯一性的理想选择。

To insert a new record into an object store, you utilize the add() method in IndexedDB. This method accepts a JavaScript object or value as its parameter and subsequently incorporates it within the designated object store. However, if there already exists an entry with identical key parameters; this operation will fail, thus rendering it ideal for guaranteeing uniqueness.

const request = objectStore.add(data);
request.onsuccess = () => {
// Handle success event
};

request.onerror = () => {
// Handle error
};

Read (Retrieve) Opeation

利用 get()方法:它从对象存储区中检索单个记录,此操作取决于您对目标条目特定键的了解。

Utilize the get() method: it retrieves a singular record from an object store, this action is contingent upon your possession of knowledge regarding the specific key for your intended entry.

通过启动一个活动光标遍历对象存储区内的记录,open Cursor()方法通过允许处理每个单独条目来提高效率和控制力;这对于遍历该对象存储区中存在的所有条目很有用。

Initiating an active cursor that iterates through records within an object store, the openCursor() method enhances efficiency and control by allowing for processing each individual entry; this proves useful in traversing all entries present in the said object store.

const request = objectStore.openCursor();
request.onsuccess = (event) => {
   const cursor = event.target.result;
   if (cursor) {
      const data = cursor.value;
      // Process data
      cursor.continue();
   }
};

request.onerror = () => {
   // Handle error
};

Update Operation

当键不存在时,put()方法更新现有记录或添加新记录。它的通用性使其适用于数据操作,特别是更新和插入。

The put() method updates an existing record or adds a new one when the key doesn’t exist. Its versatility renders it suitable for data manipulation, specifically updating and inserting.

const request = objectStore.put(data);

Delete Operation

为了根据其键从对象存储中删除特定记录,必须使用 delete() 方法;此方法提供了一种直接的删除条目方法。

To remove a specific record from an object store based on its key, one must employ the delete() method; this method offers a direct approach for deleting entries.

const request = objectStore.delete(key);

Implementation Example

上述 CRUD 操作的所有重要方法都在此代码中实现。它将数据库数据表示为一个表格,每条记录都附有删除和更新按钮。当您单击“create”按钮时,它在屏幕上显示用于创建表单的元素;从而允许将姓名和电子邮件输入到数据库中。默认情况下,此表单保持隐藏;只有在单击按钮后才会出现,并在其任务完成后消失。“prompt()”方法填充详细信息更新表单,这是为用户提供方便的功能,因为警报也会出现在同一位置。此外:相关警报用于向用户发出成功事件或错误的信号。

All the important methods of the above CRUD operation are implemented in this code. It presents database data as a table, each record accompanied by delete and update buttons. When you click on the create button, it displays an element for form creation on screen; thereby allowing entry of name and email to be inserted into the database. By default, this form remains hidden; it only appears upon button click and subsequently vanishes when its task is complete. The 'prompt()' method populates the details update form, a feature that provides convenience for users as alerts also appear in this same location. Moreover: pertinent alerts serve to signal either successful events or errors to the user.

<!DOCTYPE html>
<html>
<head>
   <style>
      body {
         font-family: Arial, sans-serif;
         margin: 20px;
      }
      h2 {
         color: #333;
      }

      button {
         padding: 8px;
         margin: 5px;
      }

      #createForm, #updateForm {
         display: none;
         border: 1px solid #ddd;
         padding: 10px;
         margin-bottom: 10px;
      }

      table {
         border-collapse: collapse;
         width: 100%;
         margin-top: 20px;
         width: auto;
      }

      th, td {
         border: 1px solid #dddddd;
         text-align: left;
         padding: 8px;
      }

      th {
         background-color: #f2f2f2;
      }
   </style>
</head>
<body>
   <h2>IndexedDB CRUD Operations</h2>
   <button onclick="showCreateForm()">Create Data</button>
   <div id="createForm" style="display: none;">
      // hidden by default, displayed on create button click
      <h3>Create Data</h3>
      <label for="name">Name:</label>
      <input type="text" id="name" required><br><br>
      <label for="email">Email:</label>
      <input type="email" id="email" required><br>
      <button onclick="createData()">Save</button>
      <button onclick="cancelCreate()">Cancel</button>
   </div>
   <table id="data-table">
      <thead>
         <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
            <th>Actions</th>
         </tr>
      </thead>
      <tbody></tbody>
   </table>
   <script>
      const dbName = "myDatabase";
      let db;
	   // Open/create indexedDB named myDatabase with version 11
      const request = window.indexedDB.open(dbName, 11);
      request.onerror = (event) => {
         alert("Database error: " + event.target.errorCode);
      };

      request.onsuccess = (event) => {
         db = event.target.result;
         showData();
      };
      request.onupgradeneeded = (event) => {
         db = event.target.result;
         const objectStore = db.createObjectStore("myObjectStore", { keyPath: "id", autoIncrement: true });
         objectStore.createIndex("name", "name", { unique: false });
         objectStore.createIndex("email", "email", { unique: true });
      };
      function showData() {
         //populates the table from the db
         const transaction = db.transaction(["myObjectStore"], "readonly");
         const objectStore = transaction.objectStore("myObjectStore");
         const tableBody = document.querySelector("#data-table tbody");
         tableBody.innerHTML = "";
         const request = objectStore.openCursor();
         request.onsuccess = (event) => {
            const cursor = event.target.result;
            if (cursor) {
               const row = tableBody.insertRow();
               row.insertCell(0).textContent = cursor.value.id;
               row.insertCell(1).textContent = cursor.value.name;
               row.insertCell(2).textContent = cursor.value.email;
               const actionsCell = row.insertCell(3);
               //update & delete is performed on the basis of id, hence id is passed as parameter to functions when corresponding button is clicked
               actionsCell.innerHTML = `
               <button onclick="showUpdateForm(${cursor.value.id})">Update</button>
               <button onclick="deleteData(${cursor.value.id})">Delete</button>
                    `;
               cursor.continue();
            }
         };
      }

      function createData() {
         const transaction = db.transaction(["myObjectStore"], "readwrite");
         const objectStore = transaction.objectStore("myObjectStore");
         const name = document.getElementById("name").value;
         const email = document.getElementById("email").value;

         const newData = {
            name: name,
            email: email
         };

         const request = objectStore.add(newData);

         request.onsuccess = () => {
            showData();
            cancelCreate();
            alert("Data added successfully")
         };

         request.onerror = () => {
            alert("Error creating data.");
         };
      }

      function showCreateForm() {
         document.getElementById("createForm").style.display = "block";
      }

      function cancelCreate() {
      //if the user decides to not insert the record.
      document.getElementById("createForm").style.display = "none";
         document.getElementById("name").value = "";
         document.getElementById("email").value = "";
      }

      function showUpdateForm(id) {
         const transaction = db.transaction(["myObjectStore"], "readwrite");
         const objectStore = transaction.objectStore("myObjectStore");

         const request = objectStore.get(id);

         request.onsuccess = (event) => {
            const data = event.target.result;

            if (data) {
               const name = prompt("Update Name:", data.name);
               const email = prompt("Update Email:", data.email);

               if (name !== null && email !== null) {
                  data.name = name;
                  data.email = email;

                  const updateRequest = objectStore.put(data);

                  updateRequest.onsuccess = () => {
                     showData();
                     alert("Updated data for "+data.name)
                  };

                  updateRequest.onerror = () => {
                     alert("Error updating data.");
                  };
               }
            }
         };
      }

      function deleteData(id) {
         const transaction = db.transaction(["myObjectStore"], "readwrite");
         const objectStore = transaction.objectStore("myObjectStore");

         const request = objectStore.delete(id);

         request.onsuccess = () => {
            showData();
            alert("Deleted data for id "+id)
         };

         request.onerror = () => {
            alert("Error deleting data.");
         };
      }

   </script>
</body>
</html>

尝试运行上述程序。在输出中,您将获得一个创建、更新和删除数据的界面。新创建或更新的数据将在操作完成后以表格形式显示。

Try running the above program. In the output, you will get an interface to create, update, and delete data. The newly created or updated data will appear in tabular form just after the operation is finished.