ข้อมูลออฟไลน์

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

พื้นที่เก็บข้อมูล

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

  • IndexedDB: ตัวเลือกพื้นที่เก็บข้อมูลออบเจ็กต์ NoSQL สำหรับ Structured Data และ Blob (ข้อมูลไบนารี)
  • WebStorage: วิธีจัดเก็บคู่คีย์/ค่าสตริงโดยใช้พื้นที่เก็บข้อมูลในเครื่องหรือพื้นที่เก็บข้อมูลเซสชัน ไม่พร้อมใช้งานในบริบทของ Service Worker API นี้เป็นการทำงานพร้อมกัน จึงไม่แนะนำให้ใช้กับพื้นที่เก็บข้อมูลที่ซับซ้อน
  • พื้นที่เก็บข้อมูลแคช: ตามที่ระบุไว้ในโมดูลการแคช

คุณสามารถจัดการพื้นที่เก็บข้อมูลของอุปกรณ์ทั้งหมดได้ด้วย Storage Manager API ในแพลตฟอร์มที่รองรับ Cache Storage API และ IndexedDB ให้การเข้าถึงพื้นที่เก็บข้อมูลถาวรแบบไม่พร้อมกันสำหรับ PWA และเข้าถึงได้จากเทรดหลัก เว็บผู้ปฏิบัติงาน และ Service Worker ทั้งคู่มีบทบาทสำคัญในการทำให้ PWA ทำงานได้อย่างเสถียรเมื่อเครือข่ายมีปัญหาหรือไม่มีอยู่จริง แต่คุณควรใช้แต่ละอย่างเมื่อใด

ใช้ Cache Storage API สำหรับทรัพยากรเครือข่าย สิ่งที่คุณจะเข้าถึงได้โดยขอผ่าน URL เช่น HTML, CSS, JavaScript, รูปภาพ, วิดีโอ และเสียง

ใช้ IndexedDB เพื่อจัดเก็บข้อมูลที่มีโครงสร้าง ซึ่งรวมถึงข้อมูลที่ต้องค้นหาหรือรวมได้ในลักษณะคล้าย NoSQL หรือข้อมูลอื่นๆ เช่น ข้อมูลที่เจาะจงผู้ใช้ซึ่งไม่จำเป็นต้องตรงกับคำขอ URL โปรดทราบว่า IndexedDB ไม่ได้ออกแบบมาสำหรับการค้นหาข้อความแบบเต็ม

IndexedDB

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

ตัวอย่างต่อไปนี้สร้างฐานข้อมูลที่เก็บสูตรอาหาร

การสร้างและเปิดฐานข้อมูล

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

  1. ใช้ฟังก์ชัน openDB เพื่อสร้างฐานข้อมูล IndexedDB ใหม่ชื่อ cookbook เนื่องจากฐานข้อมูล IndexedDB มีการกำหนดเวอร์ชัน คุณจึงต้องเพิ่มหมายเลขเวอร์ชันทุกครั้งที่ทำการเปลี่ยนแปลงในโครงสร้างฐานข้อมูล พารามิเตอร์ที่ 2 คือเวอร์ชันฐานข้อมูล ในตัวอย่างมีการตั้งค่าเป็น 1
  2. ระบบจะส่งออบเจ็กต์การเริ่มต้นที่มีโค้ดเรียกกลับ upgrade() ไปยัง openDB() ระบบจะเรียกใช้ฟังก์ชันเรียกกลับเมื่อติดตั้งฐานข้อมูลเป็นครั้งแรก หรือเมื่ออัปเกรดเป็นเวอร์ชันใหม่ ฟังก์ชันนี้เป็นเพียงที่เดียวที่จะดำเนินการต่างๆ ได้ การดำเนินการอาจรวมถึงการสร้างที่เก็บออบเจ็กต์ใหม่ (โครงสร้างที่ IndexedDB ใช้เพื่อจัดระเบียบข้อมูล) หรือดัชนี (ที่คุณต้องการค้นหา) ซึ่งเป็นจุดที่ควรมีการย้ายข้อมูลเช่นกัน โดยปกติแล้ว ฟังก์ชัน upgrade() จะมีคำสั่ง switch โดยไม่มีคำสั่ง break เพื่อให้แต่ละขั้นตอนทำงานตามลำดับ โดยขึ้นอยู่กับฐานข้อมูลเวอร์ชันเก่า
import { openDB } from 'idb';

async function createDB() {
  // Using https://github.com/jakearchibald/idb
  const db = await openDB('cookbook', 1, {
    upgrade(db, oldVersion, newVersion, transaction) {
      // Switch over the oldVersion, *without breaks*, to allow the database to be incrementally upgraded.
    switch(oldVersion) {
     case 0:
       // Placeholder to execute when database is created (oldVersion is 0)
     case 1:
       // Create a store of objects
       const store = db.createObjectStore('recipes', {
         // The `id` property of the object will be the key, and be incremented automatically
           autoIncrement: true,
           keyPath: 'id'
       });
       // Create an index called `name` based on the `type` property of objects in the store
       store.createIndex('type', 'type');
     }
   }
  });
}

ตัวอย่างนี้สร้างที่เก็บออบเจ็กต์ภายในฐานข้อมูล cookbook ชื่อ recipes โดยมีการตั้งค่าพร็อพเพอร์ตี้ id เป็นคีย์ดัชนีของร้านค้าและสร้างดัชนีอีกรายการชื่อ type โดยอิงตามพร็อพเพอร์ตี้ type

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

Safari และ Chrome ที่แสดงเนื้อหา IndexedDB

การเพิ่มข้อมูล

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

  1. เริ่มทำธุรกรรมด้วยสกุลเงิน mode ที่ตั้งค่าเป็น readwrite
  2. รับที่เก็บออบเจ็กต์ซึ่งใช้เพิ่มข้อมูล
  3. โทรหา add() โดยให้ข้อมูลที่คุณกำลังบันทึก เมธอดจะรับข้อมูลในรูปแบบพจนานุกรม (เป็นคู่คีย์/ค่า) และเพิ่มลงในที่เก็บออบเจ็กต์ ต้องโคลนพจนานุกรมได้โดยใช้การโคลนที่มีโครงสร้าง หากคุณต้องการอัปเดตออบเจ็กต์ที่มีอยู่ ให้เรียกใช้เมธอด put() แทน

ธุรกรรมมีสัญญา done ที่จะแก้ไขเมื่อธุรกรรมเสร็จสมบูรณ์ หรือปฏิเสธข้อผิดพลาดในการทำธุรกรรม

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

// Using https://github.com/jakearchibald/idb
async function addData() {
  const cookies = {
      name: "Chocolate chips cookies",
      type: "dessert"
        cook_time_minutes: 25
  };
  const tx = await db.transaction('recipes', 'readwrite');
  const store = tx.objectStore('recipes');
  store.add(cookies);
  await tx.done;
}

เมื่อเพิ่มคุกกี้แล้ว สูตรดังกล่าวจะยังคงอยู่ในฐานข้อมูลพร้อมกับสูตรอาหารอื่นๆ ระบบจะตั้งค่าและเพิ่มรหัสโดยอัตโนมัติด้วย IndexdDB หากเรียกใช้โค้ดนี้ 2 ครั้ง คุณจะมีรายการคุกกี้ที่เหมือนกัน 2 รายการ

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

ต่อไปนี้คือวิธีรับข้อมูลจาก IndexedDB

  1. เริ่มธุรกรรมและระบุที่เก็บออบเจ็กต์หรือประเภทธุรกรรม (ไม่บังคับ)
  2. โทร objectStore() จากธุรกรรมดังกล่าว ตรวจสอบว่าคุณระบุชื่อที่เก็บออบเจ็กต์แล้ว
  3. โทรหา get() ที่แจ้งกุญแจที่คุณต้องการรับ โดยค่าเริ่มต้น Store จะใช้คีย์เป็นดัชนี
// Using https://github.com/jakearchibald/idb
async function getData() {
  const tx = await db.transaction('recipes', 'readonly')
  const store = tx.objectStore('recipes');
// Because in our case the `id` is the key, we would
// have to know in advance the value of the id to
// retrieve the record
  const value = await store.get([id]);
}

ตัวจัดการพื้นที่เก็บข้อมูล

การรู้วิธีจัดการพื้นที่เก็บข้อมูลของ PWA สำคัญอย่างยิ่งต่อการจัดเก็บและการสตรีมการตอบสนองของเครือข่ายอย่างถูกต้อง

ตัวเลือกพื้นที่เก็บข้อมูลทั้งหมดจะใช้ร่วมกันได้ ซึ่งรวมถึงพื้นที่เก็บข้อมูลแคช, IndexedDB, พื้นที่เก็บข้อมูลบนเว็บ หรือแม้แต่ไฟล์ Service Worker และทรัพยากร Dependency อย่างไรก็ตาม ปริมาณพื้นที่เก็บข้อมูลที่มีจะแตกต่างกันไปในแต่ละเบราว์เซอร์ พื้นที่เก็บข้อมูลของคุณไม่จำกัด เว็บไซต์สามารถจัดเก็บข้อมูลเป็นเมกะไบต์หรือแม้แต่กิกะไบต์ได้ในบางเบราว์เซอร์ เช่น Chrome ช่วยให้เบราว์เซอร์ใช้พื้นที่ในดิสก์ได้มากถึง 80% จากพื้นที่ในดิสก์ทั้งหมด และแต่ละต้นทางก็ใช้พื้นที่ในดิสก์ได้มากถึง 60% สำหรับเบราว์เซอร์ที่รองรับ Storage API คุณสามารถดูปริมาณพื้นที่เก็บข้อมูลที่ยังเหลืออยู่สำหรับแอป โควต้า และการใช้งาน ตัวอย่างต่อไปนี้ใช้ Storage API เพื่อรับโควต้าและการใช้งานโดยประมาณ จากนั้นจะคำนวณเปอร์เซ็นต์ที่ใช้และไบต์ที่เหลือ โปรดทราบว่า navigator.storage จะส่งกลับอินสแตนซ์ของ StorageManager โดยมีอินเทอร์เฟซ Storage แยกต่างหากซึ่งอาจทำให้ผู้ใช้สับสนได้ง่าย

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

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

เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ในแอปพลิเคชัน ส่วนล้างพื้นที่เก็บข้อมูล

Firefox และ Safari ไม่มีหน้าจอข้อมูลสรุปสำหรับการดูโควต้าพื้นที่เก็บข้อมูลและการใช้งานทั้งหมดของต้นทางปัจจุบัน

ความต่อเนื่องของข้อมูล

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

หากต้องการขอพื้นที่เก็บข้อมูลถาวร โปรดเรียกใช้ StorageManager.persist() อินเทอร์เฟซ StorageManager ยังคงเข้าถึงได้ผ่านพร็อพเพอร์ตี้ navigator.storage เช่นเดิม

async function persistData() {
  if (navigator.storage && navigator.storage.persist) {
    const result = await navigator.storage.persist();
    console.log(`Data persisted: ${result}`);
}

นอกจากนี้ คุณยังตรวจสอบได้ว่ามีพื้นที่เก็บข้อมูลถาวรในต้นทางปัจจุบันแล้วหรือยังโดยเรียกใช้ StorageManager.persisted() Firefox ขอสิทธิ์จากผู้ใช้เพื่อใช้พื้นที่เก็บข้อมูลถาวร เบราว์เซอร์แบบ Chromium จะอนุญาตหรือปฏิเสธการคงอยู่ตามการเรียนรู้เพื่อระบุความสำคัญของเนื้อหาสำหรับผู้ใช้ เกณฑ์หนึ่งสำหรับ Google Chrome เช่น การติดตั้ง PWA หากผู้ใช้ติดตั้งไอคอนสำหรับ PWA ในระบบปฏิบัติการ เบราว์เซอร์อาจมอบพื้นที่เก็บข้อมูลถาวร

Mozilla Firefox ขอสิทธิ์ความต่อเนื่องของพื้นที่เก็บข้อมูลจากผู้ใช้

การรองรับเบราว์เซอร์ API

พื้นที่เก็บข้อมูลเว็บ

การสนับสนุนเบราว์เซอร์

  • 4
  • 12
  • 3.5
  • 4

แหล่งที่มา

การเข้าถึงระบบไฟล์

การสนับสนุนเบราว์เซอร์

  • 86
  • 86
  • 111
  • 15.2

แหล่งที่มา

ตัวจัดการพื้นที่เก็บข้อมูล

การสนับสนุนเบราว์เซอร์

  • 55
  • 79
  • 57
  • 15.2

แหล่งที่มา

แหล่งข้อมูล