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

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

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

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

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

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

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

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

IndexedDB

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

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

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

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

  1. ใช้ฟังก์ชัน openDB เพื่อสร้างฐานข้อมูล IndexedDB ใหม่ชื่อ cookbook เนื่องจากฐานข้อมูล IndexedDB มีการกำหนดเวอร์ชัน คุณจึงต้องเพิ่มหมายเลขเวอร์ชันทุกครั้งที่ทำการเปลี่ยนแปลงโครงสร้างฐานข้อมูล พารามิเตอร์ที่ 2 คือเวอร์ชันฐานข้อมูล ในตัวอย่างนี้ตั้งค่าเป็น 1
  2. ระบบจะส่งออบเจ็กต์การเริ่มต้นที่มี upgrade() Callback ไปยัง openDB() ระบบจะเรียกใช้ฟังก์ชัน Callback เมื่อติดตั้งฐานข้อมูลเป็นครั้งแรกหรือเมื่ออัปเกรดเป็นเวอร์ชันใหม่ ฟังก์ชันนี้เป็นที่เดียวที่การดำเนินการจะเกิดขึ้นได้ การดำเนินการอาจรวมถึงการสร้างที่เก็บออบเจ็กต์ใหม่ (โครงสร้างที่ 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

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

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

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

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

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

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

ดังที่เอกสารประกอบของไลบรารี 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;
}

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

การดึงข้อมูล

วิธีรับข้อมูลจาก IndexedDB มีดังนี้

  1. เริ่มธุรกรรมและระบุที่เก็บข้อมูลออบเจ็กต์หรือที่เก็บข้อมูลหลายรายการ และประเภทธุรกรรม (ไม่บังคับ)
  2. โทรหา objectStore() จากธุรกรรมนั้น ตรวจสอบว่าคุณได้ระบุชื่อที่เก็บข้อมูลออบเจ็กต์แล้ว
  3. โทรหา get() พร้อมคีย์ที่คุณต้องการรับ โดยค่าเริ่มต้น ร้านค้าจะใช้คีย์เป็นดัชนี
// 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 มีความสำคัญอย่างยิ่งต่อการจัดเก็บและสตรีมการตอบกลับของเครือข่ายอย่างถูกต้อง

ความจุของพื้นที่เก็บข้อมูลจะใช้ร่วมกันในตัวเลือกพื้นที่เก็บข้อมูลทั้งหมด ซึ่งรวมถึง Cache Storage, IndexedDB, Web Storage รวมถึงไฟล์ 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 DevTools คุณจะดูโควต้าของเว็บไซต์และปริมาณพื้นที่เก็บข้อมูลที่ใช้ได้โดยแยกตามสิ่งที่ใช้พื้นที่เก็บข้อมูลนั้นๆ โดยเปิดส่วนพื้นที่เก็บข้อมูลในแท็บแอปพลิเคชัน

เครื่องมือสำหรับนักพัฒนาเว็บใน 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

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

Browser Support

  • Chrome: 4.
  • Edge: 12.
  • Firefox: 3.5.
  • Safari: 4.

Source

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

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

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

Browser Support

  • Chrome: 55.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 15.2.

Source

แหล่งข้อมูล