ใช้งาน IndexedDB

คู่มือนี้ครอบคลุมข้อมูลพื้นฐานเกี่ยวกับ IndexedDB API เราใช้โค้ดสำหรับ Jake Archibald คำมั่นสัญญาของ IndexedDB ซึ่งคล้ายกับ IndexedDB API มาก แต่จะใช้สัญญา คุณสามารถawaitเพื่อให้ได้ไวยากรณ์ที่กระชับยิ่งขึ้น ซึ่งจะช่วยลดความซับซ้อนของ API การดูแลรักษาโครงสร้าง

IndexedDB เป็นระบบการจัดเก็บ NoSQL ขนาดใหญ่ที่อนุญาตให้มีการจัดเก็บ เกี่ยวกับทุกอย่างในเบราว์เซอร์ของผู้ใช้ นอกเหนือจากการค้นหา การเรียกดู และ การดำเนินการ IndexedDB ยังสนับสนุนธุรกรรม และเหมาะสำหรับ จัดเก็บข้อมูลที่มีโครงสร้างจำนวนมาก

ฐานข้อมูล IndexedDB แต่ละรายการจะไม่ซ้ำกันสำหรับต้นทาง (มักจะเป็นโดเมนเว็บไซต์หรือโดเมนย่อย) ซึ่งหมายความว่าไม่สามารถเข้าถึงหรือเข้าถึง โดยต้นทางอื่นใด ขีดจำกัดของพื้นที่เก็บข้อมูล มักมีขนาดใหญ่ หากมีอยู่ แต่เบราว์เซอร์ที่ต่างกันมีขีดจำกัด และการปลดข้อมูลแตกต่างกัน ดูส่วนอ่านเพิ่มเติมสำหรับ ข้อมูลเพิ่มเติม

ข้อกำหนดของ IndexedDB

ฐานข้อมูล
ระดับสูงสุดของ IndexedDB ไฟล์มีออบเจ็กต์ที่จัดเก็บ ซึ่งในทางกลับกัน เก็บข้อมูลที่คุณต้องการเก็บไว้ คุณสามารถสร้างฐานข้อมูลได้หลายรายการด้วย ชื่อใดก็ตามที่คุณเลือก
Object Store
ที่เก็บข้อมูลแยกสำหรับจัดเก็บข้อมูล คล้ายกับตารางในฐานข้อมูลเชิงสัมพันธ์ โดยปกติแล้วจะมีที่เก็บออบเจ็กต์ 1 รายการสำหรับแต่ละประเภท (ไม่ใช่ข้อมูล JavaScript ประเภท) ของข้อมูลที่จัดเก็บไว้ ข้อมูล JavaScript ซึ่งต่างจากตารางฐานข้อมูล ประเภทข้อมูลในร้านค้าไม่จำเป็นต้องสอดคล้องกัน เช่น หากแอป มีที่เก็บออบเจ็กต์ people ที่มีข้อมูลเกี่ยวกับผู้ใช้ 3 คน พร็อพเพอร์ตี้อายุของผู้ใช้อาจเป็น 53, 'twenty-five' และ unknown
ดัชนี
ประเภทออบเจ็กต์ที่จัดเก็บสำหรับจัดระเบียบข้อมูลในแหล่งเก็บออบเจ็กต์อื่น (เรียกว่า จัดเก็บออบเจ็กต์อ้างอิง) ตามพร็อพเพอร์ตี้แต่ละรายการของข้อมูล มีการใช้ดัชนี เพื่อเรียกข้อมูลระเบียนในที่เก็บออบเจ็กต์ตามพร็อพเพอร์ตี้นี้ ตัวอย่างเช่น หากคุณ เก็บข้อมูลผู้คนไว้ คุณอาจต้องเรียกข้อมูลในภายหลังตามชื่อ อายุ หรือ เป็นสัตว์ที่ชอบ
การดำเนินการ
การโต้ตอบกับฐานข้อมูล
ธุรกรรม
Wrapper ที่เกี่ยวข้องกับการดำเนินการหรือกลุ่มการดำเนินการที่ช่วยให้มีฐานข้อมูล ความสมบูรณ์ หากการดำเนินการในธุรกรรมทำไม่สำเร็จ การดำเนินการเหล่านั้นจะไม่เป็น ใช้แล้ว และฐานข้อมูลจะกลับไปเป็นสถานะก่อนการทำธุรกรรม เริ่มต้นขึ้น การดำเนินการอ่านหรือเขียนทั้งหมดใน IndexedDB ต้องเป็นส่วนหนึ่งของธุรกรรม การดำเนินการนี้ช่วยให้ดำเนินการอ่าน-แก้ไข-เขียนแบบอะตอมมิกได้โดยไม่เสี่ยงต่อการเกิดข้อขัดแย้ง กับชุดข้อความอื่นๆ ที่ทำงานกับฐานข้อมูลในเวลาเดียวกัน
เคอร์เซอร์
กลไกสำหรับทำซ้ำในหลายระเบียนในฐานข้อมูล

วิธีตรวจหาการรองรับ IndexedDB

IndexedDB เกือบรองรับทั้งหมดแล้ว อย่างไรก็ตาม หากคุณใช้งานเบราว์เซอร์รุ่นเก่า ก็ไม่ใช่ความคิดที่ดี รองรับฟังก์ชันตรวจหาฟีเจอร์ วิธีที่ง่ายที่สุดคือการตรวจสอบ window ออบเจ็กต์:

function indexedDBStuff () {
  // Check for IndexedDB support:
  if (!('indexedDB' in window)) {
    // Can't use IndexedDB
    console.log("This browser doesn't support IndexedDB");
    return;
  } else {
    // Do IndexedDB stuff here:
    // ...
  }
}

// Run IndexedDB code:
indexedDBStuff();

วิธีเปิดฐานข้อมูล

คุณสร้างฐานข้อมูลหลายรายการโดยใช้ชื่อที่คุณเลือกได้ด้วย IndexedDB ถ้า ฐานข้อมูลไม่มีอยู่จริงขณะที่พยายามเปิด ฐานข้อมูลนั้น ที่สร้างขึ้นโดยอัตโนมัติ หากต้องการเปิดฐานข้อมูล ให้ใช้เมธอด openDB() จากไลบรารี idb ดังนี้

import {openDB} from 'idb';

async function useDB () {
  // Returns a promise, which makes `idb` usable with async-await.
  const dbPromise = await openDB('example-database', version, events);
}

useDB();

วิธีนี้จะส่งคืนสัญญาที่แปลงเป็นออบเจ็กต์ฐานข้อมูล เมื่อใช้ เมธอด openDB() ระบุชื่อ หมายเลขเวอร์ชัน และออบเจ็กต์เหตุการณ์ที่จะตั้งค่า ฐานข้อมูล

นี่คือตัวอย่างของเมธอด openDB() ในบริบท

import {openDB} from 'idb';

async function useDB () {
  // Opens the first version of the 'test-db1' database.
  // If the database does not exist, it will be created.
  const dbPromise = await openDB('test-db1', 1);
}

useDB();

วางการตรวจหาการสนับสนุน IndexedDB ไว้ที่ด้านบนของฟังก์ชันที่ไม่ระบุชื่อ ช่วงเวลานี้ จะออกจากฟังก์ชันหากเบราว์เซอร์ไม่สนับสนุน IndexedDB หากฟังก์ชันสามารถ ดำเนินการต่อ การดำเนินการนี้จะเรียกเมธอด openDB() เพื่อเปิดฐานข้อมูลชื่อ 'test-db1' ในตัวอย่างนี้ ออบเจ็กต์เหตุการณ์ที่ไม่บังคับถูกนำออกเพื่อเก็บข้อมูล เรียบง่าย แต่คุณต้องระบุให้ทำงานกับ IndexedDB ได้อย่างมีความหมาย

วิธีทำงานกับ Object Store

ฐานข้อมูล IndexedDB มีที่เก็บออบเจ็กต์อย่างน้อย 1 รายการ ซึ่งแต่ละรายการมี สำหรับคีย์หนึ่ง และอีกคอลัมน์สำหรับข้อมูลที่เชื่อมโยงกับคีย์นั้น

สร้างที่เก็บออบเจ็กต์

ฐานข้อมูล IndexedDB ที่มีโครงสร้างดีควรมีที่เก็บออบเจ็กต์ 1 รายการสำหรับแต่ละประเภท ข้อมูลที่ต้องการคงไว้ ตัวอย่างเช่น เว็บไซต์ที่คงผู้ใช้ไว้ โปรไฟล์และโน้ตอาจมี people Object Store ที่มี person และที่เก็บออบเจ็กต์ notes รายการที่มี note ออบเจ็กต์

หากต้องการตรวจสอบความสมบูรณ์ของฐานข้อมูล คุณจะสร้างหรือนำร้านค้าออบเจ็กต์ออกเฉพาะในส่วน ในการเรียก openDB() ออบเจ็กต์เหตุการณ์แสดง upgrade() ซึ่งจะช่วยให้คุณสร้างที่เก็บออบเจ็กต์ได้ เรียกใช้ createObjectStore() ภายในเมธอด upgrade() เพื่อสร้างที่เก็บออบเจ็กต์:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('example-database', 1, {
    upgrade (db) {
      // Creates an object store:
      db.createObjectStore('storeName', options);
    }
  });
}

createStoreInDB();

เมธอดนี้ใช้ชื่อของ Object Store และการกำหนดค่าที่ไม่บังคับ ที่ช่วยให้คุณกำหนดพร็อพเพอร์ตี้ต่างๆ สำหรับที่เก็บออบเจ็กต์

ต่อไปนี้เป็นตัวอย่างวิธีใช้ createObjectStore()

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db1', 1, {
    upgrade (db) {
      console.log('Creating a new object store...');

      // Checks if the object store exists:
      if (!db.objectStoreNames.contains('people')) {
        // If the object store does not exist, create it:
        db.createObjectStore('people');
      }
    }
  });
}

createStoreInDB();

ในตัวอย่างนี้ ระบบจะส่งออบเจ็กต์เหตุการณ์ไปยังเมธอด openDB() เพื่อสร้าง และเช่นเดียวกับก่อนหน้านี้ การสร้างที่เก็บออบเจ็กต์ดังกล่าวก็จะเสร็จสิ้น ในเมธอด upgrade() ของออบเจ็กต์เหตุการณ์ แต่เนื่องจากเบราว์เซอร์แสดงข้อผิดพลาด หากคุณพยายามสร้างที่เก็บออบเจ็กต์ที่มีอยู่แล้ว เราขอแนะนำ รวมเมธอด createObjectStore() ในคำสั่ง if ที่ตรวจสอบ มี Object Store หรือไม่ ภายในบล็อก if โทร createObjectStore() เพื่อสร้างที่เก็บออบเจ็กต์ชื่อ 'firstOS'

วิธีกำหนดคีย์หลัก

เมื่อคุณกำหนดที่เก็บออบเจ็กต์ คุณสามารถกำหนดวิธีระบุข้อมูลที่ไม่ซ้ำใน Store โดยใช้คีย์หลัก คุณสามารถระบุคีย์หลักได้โดยกำหนด หรือใช้โปรแกรมสร้างคีย์

เส้นทางคีย์คือพร็อพเพอร์ตี้ที่มีอยู่และมีค่าที่ไม่ซ้ำกันอยู่เสมอ สำหรับ ตัวอย่างเช่น ในกรณีของที่เก็บออบเจ็กต์ people คุณอาจเลือกอีเมล เป็นเส้นทางคีย์

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        db.createObjectStore('people', { keyPath: 'email' });
      }
    }
  });
}

createStoreInDB();

ตัวอย่างนี้สร้างที่เก็บออบเจ็กต์ชื่อ 'people' และกำหนด email เป็นคีย์หลักในตัวเลือก keyPath

หรือใช้เครื่องมือสร้างคีย์ เช่น autoIncrement ก็ได้ ตัวสร้างคีย์ จะสร้างค่าที่ไม่ซ้ำกันสำหรับออบเจ็กต์ทุกรายการที่เพิ่มลงในที่เก็บออบเจ็กต์ โดยค่าเริ่มต้น หากคุณไม่ระบุคีย์ IndexedDB จะสร้างคีย์และเก็บไว้แยกต่างหาก จากข้อมูลดังกล่าว

ตัวอย่างต่อไปนี้สร้างที่เก็บออบเจ็กต์ชื่อ 'notes' และตั้งค่า คีย์หลักที่จะได้รับการกำหนดเป็นตัวเลขที่เพิ่มขึ้นโดยอัตโนมัติ:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('notes')) {
        db.createObjectStore('notes', { autoIncrement: true });
      }
    }
  });
}

createStoreInDB();

ตัวอย่างต่อไปนี้คล้ายกับตัวอย่างก่อนหน้านี้ แต่ในขณะนี้ ค่าที่เพิ่มขึ้นอัตโนมัติมีการกำหนดไว้ให้กับพร็อพเพอร์ตี้ชื่อ 'id' อย่างชัดเจน

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('logs')) {
        db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createStoreInDB();

การเลือกว่าจะใช้วิธีการใดในการกำหนดคีย์จะขึ้นอยู่กับข้อมูลของคุณ หาก ข้อมูลมีพร็อพเพอร์ตี้ที่ไม่ซ้ำกันเสมอ คุณจึงสามารถทำให้ข้อมูลดังกล่าวเป็น keyPath เพื่อ บังคับใช้ความเฉพาะตัวนี้ หรือใช้ค่าที่เพิ่มขึ้นโดยอัตโนมัติ

โค้ดต่อไปนี้สร้าง Object Store ขึ้นมา 3 รายการที่แสดงวิธีการต่างๆ การกำหนดคีย์หลักในที่เก็บออบเจ็กต์

import {openDB} from 'idb';

async function createStoresInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        db.createObjectStore('people', { keyPath: 'email' });
      }

      if (!db.objectStoreNames.contains('notes')) {
        db.createObjectStore('notes', { autoIncrement: true });
      }

      if (!db.objectStoreNames.contains('logs')) {
        db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createStoresInDB();

วิธีกำหนดดัชนี

ดัชนีเป็นที่เก็บออบเจ็กต์ประเภทหนึ่งที่ใช้ในการดึงข้อมูลจากข้อมูลอ้างอิง จัดเก็บตามพร็อพเพอร์ตี้ที่ระบุ มีดัชนีอยู่ภายในออบเจ็กต์อ้างอิง จัดเก็บและมีข้อมูลเดียวกัน แต่ใช้พร็อพเพอร์ตี้ที่ระบุเป็น เส้นทางคีย์แทนคีย์หลักของแหล่งอ้างอิง ต้องจัดทำดัชนีเมื่อ คุณจะสร้างที่เก็บออบเจ็กต์ และสามารถใช้เพื่อกำหนดข้อจำกัดที่ไม่ซ้ำสำหรับ ข้อมูลของคุณ

หากต้องการสร้างดัชนี ให้เรียกใช้ createIndex() ในอินสแตนซ์ที่เก็บออบเจ็กต์

import {openDB} from 'idb';

async function createIndexInStore() {
  const dbPromise = await openDB('storeName', 1, {
    upgrade (db) {
      const objectStore = db.createObjectStore('storeName');

      objectStore.createIndex('indexName', 'property', options);
    }
  });
}

createIndexInStore();

เมธอดนี้จะสร้างและแสดงผลออบเจ็กต์ดัชนี เมธอด createIndex() ใน อินสแตนซ์ของที่จัดเก็บออบเจ็กต์จะใช้ชื่อของดัชนีใหม่เป็นรายการแรก และอาร์กิวเมนต์ที่ 2 หมายถึงพร็อพเพอร์ตี้ในข้อมูล ดัชนี อาร์กิวเมนต์สุดท้ายให้คุณกำหนด 2 ตัวเลือกเพื่อกำหนดว่าจะให้ ดัชนีมีการดำเนินการ: unique และ multiEntry หากตั้งค่า unique เป็น true ค่า ดัชนีไม่อนุญาตให้มีค่าที่ซ้ำกันสำหรับคีย์เดียว ถัดไป multiEntry กำหนดลักษณะการทำงานของ createIndex() เมื่อพร็อพเพอร์ตี้ที่จัดทำดัชนีเป็นอาร์เรย์ ถ้า ตั้งค่าเป็น true แล้ว createIndex() จะเพิ่มรายการลงในดัชนีสำหรับแต่ละอาร์เรย์ หากไม่ใช่ ระบบจะเพิ่มรายการเดียวที่มีอาร์เรย์

เช่น

import {openDB} from 'idb';

async function createIndexesInStores () {
  const dbPromise = await openDB('test-db3', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        const peopleObjectStore = db.createObjectStore('people', { keyPath: 'email' });

        peopleObjectStore.createIndex('gender', 'gender', { unique: false });
        peopleObjectStore.createIndex('ssn', 'ssn', { unique: true });
      }

      if (!db.objectStoreNames.contains('notes')) {
        const notesObjectStore = db.createObjectStore('notes', { autoIncrement: true });

        notesObjectStore.createIndex('title', 'title', { unique: false });
      }

      if (!db.objectStoreNames.contains('logs')) {
        const logsObjectStore = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createIndexesInStores();

ในตัวอย่างนี้ ที่เก็บออบเจ็กต์ 'people' และ 'notes' มีดัชนี ถึง สร้างดัชนี ก่อนอื่นให้กำหนดผลลัพธ์ของ createObjectStore() (ออบเจ็กต์ เก็บออบเจ็กต์) ลงในตัวแปรเพื่อให้คุณเรียกใช้ createIndex() ในตัวแปรนั้นได้

วิธีทำงานกับข้อมูล

ส่วนนี้จะอธิบายวิธีการสร้าง อ่าน อัปเดต และลบข้อมูล เหล่านี้ การดำเนินการทั้งหมดเป็นแบบไม่พร้อมกัน โดยใช้สัญญาที่ IndexedDB API ใช้ คำขอ วิธีนี้จะทำให้ API ง่ายขึ้น แทนที่จะฟังเหตุการณ์ที่เกิดจาก คุณสามารถเรียก .then() ในออบเจ็กต์ฐานข้อมูลที่แสดงผลจาก openDB() วิธีการเริ่มต้นการโต้ตอบกับฐานข้อมูล หรือ await งานสร้างสรรค์

การดำเนินการกับข้อมูลทั้งหมดใน IndexedDB จะดำเนินการภายในธุรกรรม ชิ้น จะมีรูปแบบต่อไปนี้

  1. รับออบเจ็กต์ฐานข้อมูล
  2. เปิดธุรกรรมในฐานข้อมูล
  3. เปิด Object Store ในธุรกรรม
  4. ดำเนินการในที่เก็บออบเจ็กต์

ธุรกรรมเปรียบได้กับ Wrapper ที่ปลอดภัยสำหรับการดำเนินการหรือกลุ่ม การดำเนินงานได้ หากการดำเนินการอย่างใดอย่างหนึ่งภายในธุรกรรมล้มเหลว การดำเนินการทั้งหมด จะเปลี่ยนกลับ ธุรกรรมเจาะจงไปยัง Object Store อย่างน้อย 1 แห่ง ซึ่งคุณกำหนดไว้เมื่อเปิดธุรกรรม ซึ่งจะเป็นแบบอ่านอย่างเดียวหรือแบบอ่านอย่างเดียวก็ได้ และเขียน ซึ่งหมายความว่าการดำเนินการภายในธุรกรรมจะอ่านค่า หรือเปลี่ยนแปลงฐานข้อมูล

สร้างข้อมูล

หากต้องการสร้างข้อมูล ให้เรียกใช้ add() บนอินสแตนซ์ฐานข้อมูล และส่งข้อมูลที่คุณต้องการเพิ่ม add() อาร์กิวเมนต์แรกของเมธอดคือจัดเก็บออบเจ็กต์ที่คุณต้องการเพิ่มข้อมูล และ อาร์กิวเมนต์ที่ 2 คือออบเจ็กต์ที่มีช่องและข้อมูลที่เกี่ยวข้องที่คุณต้องการ ที่จะเพิ่ม ตัวอย่างที่ง่ายที่สุดที่มีการเพิ่มข้อมูล 1 แถวมีดังนี้

import {openDB} from 'idb';

async function addItemToStore () {
  const db = await openDB('example-database', 1);

  await db.add('storeName', {
    field: 'data'
  });
}

addItemToStore();

การโทร add() แต่ละครั้งจะเกิดขึ้นในธุรกรรมหนึ่งๆ ดังนั้นแม้จะมีการแก้ไขสัญญาแล้ว สำเร็จ นั่นไม่ได้หมายความว่าการดำเนินการดังกล่าวจะได้ผล เพื่อให้แน่ใจว่า ได้ดำเนินการเพิ่ม คุณต้องตรวจสอบว่า ธุรกรรมได้ดำเนินการเสร็จสมบูรณ์โดยใช้วิธี transaction.done() นี่คือ ที่สัญญาว่าจะแก้ไขเมื่อธุรกรรมเสร็จสมบูรณ์ และปฏิเสธหาก ข้อผิดพลาดในการทำธุรกรรม คุณต้องดำเนินการตรวจสอบนี้สำหรับ "การเขียน" ทั้งหมด การดำเนินงาน เพราะเป็นวิธีเดียวที่จะทราบ การเปลี่ยนแปลงของฐานข้อมูล ก็เกิดขึ้น

โค้ดต่อไปนี้แสดงการใช้เมธอด add() ภายในธุรกรรม

import {openDB} from 'idb';

async function addItemsToStore () {
  const db = await openDB('test-db4', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('foods')) {
        db.createObjectStore('foods', { keyPath: 'name' });
      }
    }
  });
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Add multiple items to the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.add({
      name: 'Sandwich',
      price: 4.99,
      description: 'A very tasty sandwich!',
      created: new Date().getTime(),
    }),
    tx.store.add({
      name: 'Eggs',
      price: 2.99,
      description: 'Some nice eggs you can cook up!',
      created: new Date().getTime(),
    }),
    tx.done
  ]);
}

addItemsToStore();

เมื่อเปิดฐานข้อมูล (และสร้างที่เก็บออบเจ็กต์ หากจำเป็น) คุณจะต้องมี เพื่อเปิดธุรกรรมโดยเรียกใช้เมธอด transaction() ในธุรกรรมนั้น วิธีนี้ จะใช้อาร์กิวเมนต์สำหรับร้านค้าที่คุณต้องการทำธุรกรรมและโหมด ในกรณีนี้เราสนใจที่จะเขียนถึงร้านค้า ดังนั้นตัวอย่างนี้ ระบุ 'readwrite'

ขั้นตอนถัดไปคือการเริ่มเพิ่มรายการลงในร้านค้าโดยเป็นส่วนหนึ่งของธุรกรรม ในตัวอย่างก่อนหน้านี้ เรากำลังดำเนินการกับการดำเนินการ 3 อย่างใน 'foods' จัดเก็บสิ่งที่แต่ละคำให้คำมั่นสัญญาไว้ ดังนี้

  1. เพิ่มระเบียนสำหรับแซนด์วิชแสนอร่อย
  2. กำลังเพิ่มระเบียนสำหรับไข่บางฟอง
  3. การส่งสัญญาณว่าธุรกรรมเสร็จสมบูรณ์แล้ว (tx.done)

เนื่องจากการดำเนินการทั้งหมดนี้เป็นไปตามสัญญา เราจึงจำเป็นต้องรอให้ เพื่อให้เสร็จสิ้น ส่งต่อคำสัญญาเหล่านี้ให้กับ Promise.all เป็นวิธีที่ดีและเหมาะกับการทำงาน ตามหลักสรีรศาสตร์ Promise.all ยอมรับอาร์เรย์ของ สัญญาที่สัญญาไว้ และจะสิ้นสุดเมื่อสัญญาทั้งหมดที่ให้ไว้หายไป

สำหรับ 2 ระเบียนที่เพิ่ม อินเทอร์เฟซ store ของอินสแตนซ์ธุรกรรม จะโทรหา add() และส่งข้อมูลไปให้ คุณสามารถawaitโทรหา Promise.all ได้ เพื่อให้ระบบสิ้นสุดเมื่อธุรกรรมเสร็จสมบูรณ์

อ่านข้อมูล

หากต้องการอ่านข้อมูล ให้เรียกใช้ get() ในอินสแตนซ์ฐานข้อมูลที่คุณเรียกโดยใช้เมธอด openDB() get() ใช้ชื่อของร้านค้าและค่าคีย์หลักของออบเจ็กต์ ที่ต้องการดึงข้อมูล ต่อไปนี้คือตัวอย่างพื้นฐาน

import {openDB} from 'idb';

async function getItemFromStore () {
  const db = await openDB('example-database', 1);

  // Get a value from the object store by its primary key value:
  const value = await db.get('storeName', 'unique-primary-key-value');
}

getItemFromStore();

เช่นเดียวกับ add() เมธอด get() จะให้คำมั่นสัญญา เพื่อให้คุณสามารถawaitใช้วิธีนี้ได้ ที่คุณต้องการ หรือใช้การเรียกกลับ .then() ตามที่สัญญาไว้

ตัวอย่างต่อไปนี้ใช้เมธอด get() ในฐานข้อมูล 'test-db4' เก็บออบเจ็กต์ 'foods' แถวเพื่อดูแถว 1 แถวด้วยคีย์หลัก 'name' ดังนี้

import {openDB} from 'idb';

async function getItemFromStore () {
  const db = await openDB('test-db4', 1);
  const value = await db.get('foods', 'Sandwich');

  console.dir(value);
}

getItemFromStore();

การดึงแถวเดียวจากฐานข้อมูลค่อนข้างตรงไปตรงมา: เปิด ฐานข้อมูลแล้วระบุที่เก็บออบเจ็กต์และค่าคีย์หลักของแถวที่คุณ ต้องการรับข้อมูลจาก เนื่องจากเมธอด get() จะแสดงคำสัญญา คุณจะสามารถ awaitได้เลย

อัปเดตข้อมูล

หากต้องการอัปเดตข้อมูล ให้โทรหา put() ในที่เก็บออบเจ็กต์ เมธอด put() คล้ายกับเมธอด add() และยังใช้แทน add() เพื่อสร้างข้อมูลได้ด้วย ต่อไปนี้คือตัวอย่างพื้นฐาน ของการใช้ put() เพื่ออัปเดตแถวในที่เก็บออบเจ็กต์ตามค่าคีย์หลัก:

import {openDB} from 'idb';

async function updateItemInStore () {
  const db = await openDB('example-database', 1);

  // Update a value from in an object store with an inline key:
  await db.put('storeName', { inlineKeyName: 'newValue' });

  // Update a value from in an object store with an out-of-line key.
  // In this case, the out-of-line key value is 1, which is the
  // auto-incremented value.
  await db.put('otherStoreName', { field: 'value' }, 1);
}

updateItemInStore();

วิธีนี้ได้ผลเช่นเดียวกับวิธีการอื่นๆ นอกจากนี้คุณยังใช้ put() เป็น ส่วนหนึ่งของธุรกรรม นี่คือตัวอย่างการใช้ร้านค้า 'foods' จากก่อนหน้านี้ ที่อัปเดตราคาของแซนด์วิชและไข่ เช่น

import {openDB} from 'idb';

async function updateItemsInStore () {
  const db = await openDB('test-db4', 1);
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Update multiple items in the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.put({
      name: 'Sandwich',
      price: 5.99,
      description: 'A MORE tasty sandwich!',
      updated: new Date().getTime() // This creates a new field
    }),
    tx.store.put({
      name: 'Eggs',
      price: 3.99,
      description: 'Some even NICER eggs you can cook up!',
      updated: new Date().getTime() // This creates a new field
    }),
    tx.done
  ]);
}

updateItemsInStore();

วิธีอัปเดตรายการจะขึ้นอยู่กับวิธีที่คุณตั้งค่าคีย์ หากคุณตั้งค่า keyPath แต่ละแถวในที่เก็บออบเจ็กต์จะเชื่อมโยงกับคีย์ในบรรทัด ก่อนหน้านี้ ตัวอย่างจะอัปเดตแถวตามคีย์นี้ และเมื่อคุณอัปเดตแถวใน คุณจะต้องระบุคีย์นั้นเพื่ออัปเดตรายการที่เหมาะสมใน ออบเจ็กต์สโตร์ นอกจากนี้คุณยังสร้างคีย์นอกบรรทัดได้โดยการตั้งค่า autoIncrement เป็นคีย์หลัก

ลบข้อมูล

หากต้องการลบข้อมูล ให้เรียก delete() ในที่เก็บออบเจ็กต์

import {openDB} from 'idb';

async function deleteItemFromStore () {
  const db = await openDB('example-database', 1);

  // Delete a value 
  await db.delete('storeName', 'primary-key-value');
}

deleteItemFromStore();

คุณใช้รายการนี้เป็นส่วนหนึ่งของธุรกรรมได้ เช่น add() และ put()

import {openDB} from 'idb';

async function deleteItemsFromStore () {
  const db = await openDB('test-db4', 1);
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Delete multiple items from the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.delete('Sandwich'),
    tx.store.delete('Eggs'),
    tx.done
  ]);
}

deleteItemsFromStore();

โครงสร้างของการโต้ตอบฐานข้อมูลจะเหมือนกับ การดำเนินงาน โปรดอย่าลืมตรวจสอบว่าการทำธุรกรรมทั้งหมดเสร็จสมบูรณ์ภายในวันที่ รวมถึงเมธอด tx.done ในอาร์เรย์ที่คุณส่งไปยัง Promise.all

กำลังดึงข้อมูลทั้งหมด

จนถึงตอนนี้คุณเรียกออบเจ็กต์จาก Store ได้ทีละ 1 รายการเท่านั้น นอกจากนี้คุณยัง เรียกดูข้อมูลทั้งหมดหรือบางส่วนจากที่เก็บออบเจ็กต์หรือดัชนีโดยใช้ เมธอด getAll() หรือเคอร์เซอร์ก็ได้

เมธอด getAll()

วิธีที่ง่ายที่สุดในการเรียกข้อมูลทั้งหมดของที่เก็บออบเจ็กต์คือการเรียกใช้ getAll() ในออบเจ็กต์สโตร์หรือดัชนี ดังนี้

import {openDB} from 'idb';

async function getAllItemsFromStore () {
  const db = await openDB('test-db4', 1);

  // Get all values from the designated object store:
  const allValues = await db.getAll('storeName');

  console.dir(allValues);
}

getAllItemsFromStore();

วิธีนี้จะแสดงออบเจ็กต์ทั้งหมดในที่เก็บออบเจ็กต์โดยไม่มีข้อจำกัด แต่อย่างใด ซึ่งเป็นวิธีที่ตรงที่สุดในการรับค่าทั้งหมดจาก Object Store แต่ยืดหยุ่นน้อยที่สุดด้วย

import {openDB} from 'idb';

async function getAllItemsFromStore () {
  const db = await openDB('test-db4', 1);

  // Get all values from the designated object store:
  const allValues = await db.getAll('foods');

  console.dir(allValues);
}

getAllItemsFromStore();

ตัวอย่างนี้เรียกใช้ getAll() ในที่เก็บออบเจ็กต์ 'foods' ซึ่งจะแสดงผลลัพธ์ทั้งหมด ออบเจ็กต์จาก 'foods' เรียงลำดับตามคีย์หลัก

วิธีใช้เคอร์เซอร์

เคอร์เซอร์เป็นวิธีที่มีความยืดหยุ่นมากขึ้นในการดึงข้อมูลวัตถุหลายรายการ เคอร์เซอร์เลือก ออบเจ็กต์แต่ละรายการในออบเจ็กต์ที่จัดเก็บหรือจัดทำดัชนีทีละรายการ ซึ่งจะช่วยให้คุณทำสิ่งต่างๆ ได้ ไว้กับข้อมูลที่เลือกไว้ เคอร์เซอร์เหมือนกับการดำเนินการฐานข้อมูลอื่นๆ ในการทำธุรกรรม

หากต้องการสร้างเคอร์เซอร์ ให้เรียกใช้ openCursor() ใน Object Store โดยเป็นส่วนหนึ่งของธุรกรรม ใช้ร้านค้า 'foods' จาก ตัวอย่างก่อนหน้านี้ แสดงวิธีเลื่อนเคอร์เซอร์ผ่านแถวข้อมูลทั้งหมดใน ออบเจ็กต์ที่จัดเก็บ:

import {openDB} from 'idb';

async function getAllItemsFromStoreWithCursor () {
  const db = await openDB('test-db4', 1);
  const tx = await db.transaction('foods', 'readonly');

  // Open a cursor on the designated object store:
  let cursor = await tx.store.openCursor();

  // Iterate on the cursor, row by row:
  while (cursor) {
    // Show the data in the row at the current cursor position:
    console.log(cursor.key, cursor.value);

    // Advance the cursor to the next row:
    cursor = await cursor.continue();
  }
}

getAllItemsFromStoreWithCursor();

ธุรกรรมในกรณีนี้จะเปิดขึ้นในโหมด 'readonly' และ โดยเรียกใช้เมธอด openCursor ในการวนซ้ำ while ที่ตามมา แถวที่ ตำแหน่งปัจจุบันของเคอร์เซอร์อาจมีการอ่านคุณสมบัติ key และ value และ คุณสามารถดำเนินการกับค่าเหล่านั้นในรูปแบบใดก็ได้ที่เหมาะกับ แอป เมื่อพร้อมแล้ว ให้เรียกออบเจ็กต์ cursor continue() ของ เพื่อไปยังแถวถัดไป และการวนซ้ำ while จะสิ้นสุดเมื่อเคอร์เซอร์ ถึงจุดสิ้นสุดของชุดข้อมูล

ใช้เคอร์เซอร์กับช่วงและดัชนี

ดัชนีช่วยให้คุณดึงข้อมูลในออบเจ็กต์ที่จัดเก็บโดยพร็อพเพอร์ตี้อื่นนอกเหนือจากพร็อพเพอร์ตี้ คีย์หลัก คุณสามารถสร้างดัชนีในพร็อพเพอร์ตี้ใดก็ได้ ซึ่งจะกลายเป็น keyPath สำหรับดัชนี ให้ระบุช่วงในพร็อพเพอร์ตี้นั้น และรับข้อมูลภายในแท็ก ช่วงโดยใช้ getAll() หรือเคอร์เซอร์

กำหนดช่วงโดยใช้ออบเจ็กต์ IDBKeyRange และรายการใดรายการหนึ่งต่อไปนี้ วิธีการ:

เมธอด upperBound() และ lowerBound() จะระบุขีดจำกัดสูงสุดและต่ำสุด ของช่วง

IDBKeyRange.lowerBound(indexKey);

หรือ

IDBKeyRange.upperBound(indexKey);

แต่ละรายการจะใช้อาร์กิวเมนต์ 1 รายการ ซึ่งก็คือค่า keyPath ของดัชนีสําหรับรายการที่คุณต้องการ เพื่อระบุขีดจำกัดสูงสุดหรือต่ำสุด

เมธอด bound() ระบุทั้งขีดจำกัดสูงสุดและต่ำสุด:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

ช่วงสำหรับฟังก์ชันเหล่านี้จะรวมอยู่แล้วโดยค่าเริ่มต้น ซึ่งหมายความว่าจะมี ข้อมูลที่ระบุเป็นขีดจำกัดของช่วง หากไม่ต้องการค่าดังกล่าว ระบุช่วงเป็น specific โดยการส่ง true เป็นอาร์กิวเมนต์ที่สองสำหรับ lowerBound() หรือ upperBound() หรือเป็นอาร์กิวเมนต์ที่ 3 และ 4 ของ bound() สำหรับขีดจำกัดต่ำสุดและสูงสุดตามลำดับ

ตัวอย่างถัดไปใช้ดัชนีในพร็อพเพอร์ตี้ 'price' ในออบเจ็กต์ 'foods' ตอนนี้สโตร์ยังมีแบบฟอร์มแนบอยู่ พร้อมด้วยอินพุต 2 รายการของ ขีดจำกัดบนและล่างของช่วง ใช้รหัสต่อไปนี้เพื่อค้นหาอาหารที่มี ราคาระหว่างขีดจำกัดดังกล่าว

import {openDB} from 'idb';

async function searchItems (lower, upper) {
  if (!lower === '' && upper === '') {
    return;
  }

  let range;

  if (lower !== '' && upper !== '') {
    range = IDBKeyRange.bound(lower, upper);
  } else if (lower === '') {
    range = IDBKeyRange.upperBound(upper);
  } else {
    range = IDBKeyRange.lowerBound(lower);
  }

  const db = await openDB('test-db4', 1);
  const tx = await db.transaction('foods', 'readonly');
  const index = tx.store.index('price');

  // Open a cursor on the designated object store:
  let cursor = await index.openCursor(range);

  if (!cursor) {
    return;
  }

  // Iterate on the cursor, row by row:
  while (cursor) {
    // Show the data in the row at the current cursor position:
    console.log(cursor.key, cursor.value);

    // Advance the cursor to the next row:
    cursor = await cursor.continue();
  }
}

// Get items priced between one and four dollars:
searchItems(1.00, 4.00);

โค้ดตัวอย่างจะได้รับค่าขีดจํากัดก่อน แล้วตรวจสอบว่าขีดจํากัดหรือไม่ ที่มีอยู่ โค้ดส่วนถัดไปจะกำหนดว่าจะใช้วิธีใดในการจำกัดช่วง โดยอิงตามค่า ในการโต้ตอบกับฐานข้อมูล ให้เปิดร้านค้าออบเจ็กต์บน ตามปกติ แล้วเปิดดัชนี 'price' ในที่เก็บออบเจ็กต์ ดัชนี 'price' ช่วยให้คุณค้นหาสินค้าตามราคาได้

จากนั้นรหัสจะเปิดเคอร์เซอร์ในดัชนีและส่งผ่านช่วงดังกล่าว เคอร์เซอร์ แสดงผลสัญญาที่แสดงถึงออบเจ็กต์แรกในช่วง หรือ undefined หาก ไม่มีข้อมูลภายในช่วง เมธอด cursor.continue() แสดงผล เคอร์เซอร์ที่แทนวัตถุถัดไป และวนซ้ำไปเรื่อยๆ จนกว่าคุณจะ ถึงจุดสิ้นสุดของช่วง

การกำหนดเวอร์ชันฐานข้อมูล

เมื่อเรียกใช้เมธอด openDB() คุณจะระบุหมายเลขเวอร์ชันของฐานข้อมูลได้ ในพารามิเตอร์ที่ 2 ในตัวอย่างทั้งหมดในคู่มือนี้ เวอร์ชัน ตั้งค่าเป็น 1 แต่สามารถอัปเกรดฐานข้อมูลเป็นเวอร์ชันใหม่ได้ถ้าต้องการ แก้ไขในทางใดทางหนึ่ง หากเวอร์ชันที่ระบุสูงกว่าเวอร์ชันของ ฐานข้อมูลที่มีอยู่ Callback upgrade ในออบเจ็กต์เหตุการณ์จะดำเนินการ ซึ่งช่วยให้คุณเพิ่มที่เก็บออบเจ็กต์ใหม่และดัชนีลงในฐานข้อมูลได้

ออบเจ็กต์ db ใน Callback upgrade มีพร็อพเพอร์ตี้ oldVersion พิเศษ ซึ่งระบุหมายเลขเวอร์ชันของฐานข้อมูลที่เบราว์เซอร์มีสิทธิ์เข้าถึง คุณสามารถส่งหมายเลขเวอร์ชันนี้ไปยังคำสั่ง switch เพื่อเรียกใช้บล็อกของ ภายใน Callback upgrade ตามเวอร์ชันฐานข้อมูลที่มีอยู่ หมายเลข เช่น

import {openDB} from 'idb';

const db = await openDB('example-database', 2, {
  upgrade (db, oldVersion) {
    switch (oldVersion) {
      case 0:
        // Create first object store:
        db.createObjectStore('store', { keyPath: 'name' });

      case 1:
        // Get the original object store, and create an index on it:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('name', 'name');
    }
  }
});

ตัวอย่างนี้ตั้งค่าฐานข้อมูลเวอร์ชันล่าสุดเป็น 2 เมื่อรหัสนี้ จะทำงานครั้งแรก ฐานข้อมูลยังไม่ปรากฏในเบราว์เซอร์ ดังนั้น oldVersion คือ 0 และคำสั่ง switch เริ่มต้นที่ case 0 ในตัวอย่างนี้ เพิ่มที่เก็บออบเจ็กต์ 'store' ลงในฐานข้อมูล

ประเด็นสำคัญ: ในคำสั่ง switch โดยปกติจะมี break หลัง case แต่ละครั้ง บล็อก แต่ไม่ตั้งใจใช้ในส่วนนี้ ด้วยวิธีนี้ หากโค้ดที่มีอยู่ ฐานข้อมูลเป็นเวอร์ชันที่ล่าช้ากว่า หรือหากไม่มี โค้ดจะดำเนินต่อไป ผ่านบล็อกที่เหลือของ case รายการจนกว่าจะอัปเดต ดังนั้นในตัวอย่างนี้ เบราว์เซอร์จะดำเนินการต่อจนถึง case 1 ซึ่งจะสร้างดัชนี name บน จัดเก็บออบเจ็กต์ store รายการ

หากต้องการสร้างดัชนี 'description' ในที่เก็บออบเจ็กต์ 'store' ให้อัปเดต หมายเลขเวอร์ชันและเพิ่มบล็อก case ใหม่ดังต่อไปนี้

import {openDB} from 'idb';

const db = await openDB('example-database', 3, {
  upgrade (db, oldVersion) {
    switch (oldVersion) {
      case 0:
        // Create first object store:
        db.createObjectStore('store', { keyPath: 'name' });

      case 1:
        // Get the original object store, and create an index on it:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('name', 'name');

      case 2:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('description', 'description');
    }
  }
});

หากฐานข้อมูลที่คุณสร้างในตัวอย่างก่อนหน้านี้ยังคงอยู่ในเบราว์เซอร์ เมื่อดำเนินการนี้ oldVersion จะเท่ากับ 2 เบราว์เซอร์ข้าม case 0 และ case 1 และเรียกใช้โค้ดใน case 2 ซึ่งจะสร้าง description ดัชนี หลังจากนั้น เบราว์เซอร์จะมีฐานข้อมูลที่เวอร์ชัน 3 ซึ่งมี store จัดเก็บออบเจ็กต์ที่มีดัชนี name และ description

อ่านเพิ่มเติม

แหล่งข้อมูลต่อไปนี้จะให้ข้อมูลและบริบทเพิ่มเติมในการใช้ IndexedDB

เอกสาร IndexedDB

ขีดจำกัดของพื้นที่เก็บข้อมูล